• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
17 #define ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
18 
19 #include "ecmascript/js_handle.h"
20 #include "ecmascript/js_tagged_value.h"
21 #include "ecmascript/jspandafile/class_info_extractor.h"
22 #include "ecmascript/mem/assert_scope.h"
23 #include "ecmascript/object_fast_operator.h"
24 
25 #include "ecmascript/base/array_helper.h"
26 #include "ecmascript/ecma_string_table.h"
27 #include "ecmascript/element_accessor-inl.h"
28 #include "ecmascript/global_env.h"
29 #include "ecmascript/js_api/js_api_arraylist.h"
30 #include "ecmascript/js_api/js_api_deque.h"
31 #include "ecmascript/js_api/js_api_linked_list.h"
32 #include "ecmascript/js_api/js_api_list.h"
33 #include "ecmascript/js_api/js_api_plain_array.h"
34 #include "ecmascript/js_api/js_api_queue.h"
35 #include "ecmascript/js_api/js_api_stack.h"
36 #include "ecmascript/js_api/js_api_vector.h"
37 #include "ecmascript/js_api/js_api_bitvector.h"
38 #include "ecmascript/js_date.h"
39 #include "ecmascript/js_function.h"
40 #include "ecmascript/js_hclass-inl.h"
41 #include "ecmascript/js_object-inl.h"
42 #include "ecmascript/js_tagged_value-inl.h"
43 #include "ecmascript/js_typed_array.h"
44 #include "ecmascript/message_string.h"
45 #include "ecmascript/property_attributes.h"
46 #include "ecmascript/runtime_call_id.h"
47 #include "ecmascript/shared_objects/concurrent_api_scope.h"
48 #include "ecmascript/shared_objects/js_shared_array.h"
49 #include "ecmascript/tagged_array.h"
50 #include "ecmascript/tagged_dictionary.h"
51 
52 namespace panda::ecmascript {
53 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
54 #define CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder) \
55     if (UNLIKELY((receiver) != (holder))) {           \
56         return JSTaggedValue::Hole();                 \
57     }
58 
HasOwnProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)59 std::pair<JSTaggedValue, bool> ObjectFastOperator::HasOwnProperty(JSThread *thread,
60                                                                   JSTaggedValue receiver, JSTaggedValue key)
61 {
62     [[maybe_unused]] DisallowGarbageCollection noGc;
63     if (!receiver.IsHeapObject() || !(receiver.IsRegularObject())) {
64         return std::make_pair(JSTaggedValue::Hole(), false);
65     }
66     if (!key.IsString()) {
67         return std::make_pair(JSTaggedValue::Hole(), false);
68     }
69 
70     uint32_t index = 0;
71     if (JSTaggedValue::ToElementIndex(key, &index)) {
72         ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
73         JSHandle<JSObject> receiverObj(thread, receiver);
74         if (ElementAccessor::GetElementsLength(receiverObj) == 0) {
75             return std::make_pair(JSTaggedValue::Hole(), true);  // Empty Array
76         }
77 
78         if (!ElementAccessor::IsDictionaryMode(receiverObj)) {
79             if (ElementAccessor::GetElementsLength(receiverObj) <= index) {
80                 return std::make_pair(JSTaggedValue::Hole(), true);
81             }
82             JSTaggedValue value = ElementAccessor::Get(receiverObj, index);
83             return std::make_pair(value, true);
84         } else {
85             NumberDictionary *dictionary =
86                 NumberDictionary::Cast(JSObject::Cast(receiver)->GetElements().GetTaggedObject());
87             int entry = dictionary->FindEntry(JSTaggedValue(static_cast<int>(index)));
88             if (entry == -1) {
89                 return std::make_pair(JSTaggedValue::Hole(), true);
90             }
91             return std::make_pair(JSTaggedValue::Undefined(), true);
92         }
93     }
94 
95     if (!EcmaStringAccessor(key).IsInternString()) {
96         JSHandle<EcmaString> keyHandle(thread, key);
97         EcmaString *str = thread->GetEcmaVM()->GetEcmaStringTable()->TryGetInternString(thread, keyHandle);
98         if (str == nullptr) {
99             return std::make_pair(JSTaggedValue::Hole(), true);
100         }
101         key = JSTaggedValue(str);
102     }
103     auto *hclass = receiver.GetTaggedObject()->GetClass();
104     if (LIKELY(!hclass->IsDictionaryMode())) {
105         ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
106         int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
107         if (entry != -1) {
108             return std::make_pair(JSTaggedValue::Undefined(), true);
109         }
110     } else {
111         TaggedArray *array = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject());
112         ASSERT(array->IsDictionaryMode());
113         NameDictionary *dict = NameDictionary::Cast(array);
114         int entry = dict->FindEntry(key);
115         if (entry != -1) {
116             return std::make_pair(JSTaggedValue::Undefined(), true);
117         }
118     }
119     return std::make_pair(JSTaggedValue::Hole(), true);
120 }
121 
122 template <ObjectFastOperator::Status status>
TryFastHasProperty(JSThread * thread,JSTaggedValue receiver,JSMutableHandle<JSTaggedValue> keyHandle)123 JSTaggedValue ObjectFastOperator::TryFastHasProperty(JSThread *thread, JSTaggedValue receiver,
124                                                      JSMutableHandle<JSTaggedValue> keyHandle)
125 {
126     JSTaggedValue key = keyHandle.GetTaggedValue();
127     if (UNLIKELY(!receiver.IsHeapObject() || !receiver.IsRegularObject())) {
128         return JSTaggedValue::Hole();
129     }
130     if (UNLIKELY(!key.IsNumber() && !key.IsString())) {
131         return JSTaggedValue::Hole();
132     }
133 
134     // Elements
135     auto index = TryToElementsIndex(key);
136     if (index >= 0) {
137         ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
138         JSHandle<JSObject> receiverObj(thread, receiver);
139         if (!ElementAccessor::IsDictionaryMode(receiverObj)) {
140             if (index < ElementAccessor::GetElementsLength(receiverObj)) {
141                 JSTaggedValue value = ElementAccessor::Get(receiverObj, index);
142                 return value.IsHole() ? JSTaggedValue::Hole() : JSTaggedValue::True();
143             }
144         }
145         return JSTaggedValue::Hole();
146     }
147 
148     // layout cache
149     auto *hclass = receiver.GetTaggedObject()->GetClass();
150     if (LIKELY(!hclass->IsDictionaryMode())) {
151         if (!EcmaStringAccessor(key).IsInternString()) {
152             JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
153             auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyHandle);
154             EcmaStringAccessor(string).SetInternString();
155             keyHandle.Update(JSTaggedValue(string));
156             key = keyHandle.GetTaggedValue();
157             receiver = receiverHandler.GetTaggedValue();
158         }
159         ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
160         int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
161         if (entry != -1) {
162             return JSTaggedValue::True();
163         }
164     }
165     return JSTaggedValue::Hole();
166 }
167 
168 template <ObjectFastOperator::Status status>
TryFastGetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSMutableHandle<JSTaggedValue> keyHandle)169 JSTaggedValue ObjectFastOperator::TryFastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver,
170                                                             JSMutableHandle<JSTaggedValue> keyHandle)
171 {
172     JSTaggedValue key = keyHandle.GetTaggedValue();
173     if (UNLIKELY(!receiver.IsHeapObject() || !receiver.IsRegularObject())) {
174         return JSTaggedValue::Hole();
175     }
176     if (UNLIKELY(!key.IsNumber() && !key.IsString())) {
177         return JSTaggedValue::Hole();
178     }
179     auto index = TryToElementsIndex(key);
180     if (index >= 0) {
181         return TryFastGetPropertyByIndex<status>(thread, receiver, index);
182     }
183     if (key.IsString()) {
184         if (!EcmaStringAccessor(key).IsInternString()) {
185             [[maybe_unused]] EcmaHandleScope handleScope(thread);
186             JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
187             auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyHandle);
188             EcmaStringAccessor(string).SetInternString();
189             keyHandle.Update(JSTaggedValue(string));
190             key = keyHandle.GetTaggedValue();
191             receiver = receiverHandler.GetTaggedValue();
192         }
193         auto ret = TryGetPropertyByNameThroughCacheAtLocal(thread, receiver, key);
194         if (!ret.IsHole()) {
195             return ret;
196         }
197         return ObjectFastOperator::GetPropertyByName<status>(thread, receiver, key);
198     }
199     return JSTaggedValue::Hole();
200 }
201 
202 template<ObjectFastOperator::Status status>
TryFastGetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)203 JSTaggedValue ObjectFastOperator::TryFastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
204 {
205     JSTaggedValue holder = receiver;
206     auto *hclass = holder.GetTaggedObject()->GetClass();
207     JSHandle<JSObject> currentHolder(thread, holder);
208     if (!hclass->IsDictionaryElement()) {
209         ASSERT(!ElementAccessor::IsDictionaryMode(currentHolder));
210         if (index < ElementAccessor::GetElementsLength(currentHolder)) {
211             JSTaggedValue value = ElementAccessor::Get(currentHolder, index);
212             if (!value.IsHole()) {
213                 return value;
214             }
215         }
216     }
217     return JSTaggedValue::Hole();
218 }
219 
220 template<ObjectFastOperator::Status status>
TryGetPropertyByNameThroughCacheAtLocal(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)221 JSTaggedValue ObjectFastOperator::TryGetPropertyByNameThroughCacheAtLocal(JSThread *thread, JSTaggedValue receiver,
222                                                                           JSTaggedValue key)
223 {
224     auto *hclass = receiver.GetTaggedObject()->GetClass();
225     if (LIKELY(!hclass->IsDictionaryMode())) {
226         ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
227 
228         int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
229         if (entry != -1) {
230             LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
231             PropertyAttributes attr(layoutInfo->GetAttr(entry));
232             ASSERT(static_cast<int>(attr.GetOffset()) == entry);
233             auto value = JSObject::Cast(receiver)->GetProperty(hclass, attr);
234             if (UNLIKELY(attr.IsAccessor())) {
235                 if (GetInternal(status)) {
236                     return value;
237                 }
238                 return CallGetter(thread, receiver, receiver, value);
239             }
240             ASSERT(!value.IsAccessor());
241             if (!value.IsHole()) {
242                 return value;
243             }
244         }
245     }
246     return JSTaggedValue::Hole();
247 }
248 
249 template<ObjectFastOperator::Status status>
GetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,bool noAllocate,bool * isCallGetter)250 JSTaggedValue ObjectFastOperator::GetPropertyByName(JSThread *thread, JSTaggedValue receiver,
251                                                     JSTaggedValue key, [[maybe_unused]]bool noAllocate,
252                                                     [[maybe_unused]]bool *isCallGetter)
253 {
254     INTERPRETER_TRACE(thread, GetPropertyByName);
255     // no gc when return hole
256     ASSERT(key.IsStringOrSymbol());
257     JSTaggedValue holder = receiver;
258     do {
259         auto *hclass = holder.GetTaggedObject()->GetClass();
260         JSType jsType = hclass->GetObjectType();
261         if (IsSpecialIndexedObj(jsType)) {
262             if (IsFastTypeArray(jsType)) {
263                 JSTaggedValue res = FastGetTypeArrayProperty(thread, receiver, holder, key, jsType);
264                 if (res.IsNull()) {
265                     return JSTaggedValue::Hole();
266                 } else if (UNLIKELY(!res.IsHole())) {
267                     return res;
268                 }
269             } else if (IsString(jsType) && key.IsString()) {
270                 auto vm = thread->GetEcmaVM();
271                 JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString();
272                 bool isLenKey = EcmaStringAccessor::StringsAreEqual(vm,
273                     JSHandle<EcmaString>(thread, key), JSHandle<EcmaString>(thread, lenKey));
274                 if (isLenKey) {  // get string length
275                     return JSTaggedValue(EcmaStringAccessor(holder).GetLength());
276                 } else {  // get string prototype
277                     JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
278                     JSHandle<JSTaggedValue> stringPrototype = env->GetStringPrototype();
279                     holder = stringPrototype.GetTaggedValue();
280                     continue;
281                 }
282             } else if (!IsJSPrimitiveRef(jsType)) {  // not string prototype etc.
283                 return JSTaggedValue::Hole();
284             }
285         }
286 
287         if (LIKELY(!hclass->IsDictionaryMode())) {
288             ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode());
289 
290             int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
291             if (entry != -1) {
292                 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
293                 PropertyAttributes attr(layoutInfo->GetAttr(entry));
294                 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
295                 auto value = JSObject::Cast(holder)->GetProperty(hclass, attr);
296                 if (UNLIKELY(attr.IsAccessor())) {
297                     if (GetInternal(status)) {
298                         return value;
299                     }
300                     if (noAllocate) {
301                         *isCallGetter = true;
302                         return value;
303                     }
304                     return CallGetter(thread, receiver, holder, value);
305                 }
306                 ASSERT(!value.IsAccessor());
307                 if (!value.IsHole()) {
308                     return value;
309                 }
310             }
311         } else {
312             TaggedArray *array = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject());
313             ASSERT(array->IsDictionaryMode());
314             NameDictionary *dict = NameDictionary::Cast(array);
315             int entry = dict->FindEntry(key);
316             if (entry != -1) {
317                 auto value = dict->GetValue(entry);
318                 auto attr = dict->GetAttributes(entry);
319                 if (UNLIKELY(attr.IsAccessor())) {
320                     if (GetInternal(status)) {
321                         return value;
322                     }
323                     if (noAllocate) {
324                         *isCallGetter = true;
325                         return value;
326                     }
327                     return CallGetter(thread, receiver, holder, value);
328                 }
329                 ASSERT(!value.IsAccessor());
330                 return value;
331             }
332         }
333         if (UseOwn(status)) {
334             break;
335         }
336         holder = hclass->GetPrototype();
337     } while (holder.IsHeapObject());
338     // not found
339     return JSTaggedValue::Undefined();
340 }
341 
342 template<ObjectFastOperator::Status status>
TrySetPropertyByNameThroughCacheAtLocal(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value)343 JSTaggedValue ObjectFastOperator::TrySetPropertyByNameThroughCacheAtLocal(JSThread *thread, JSTaggedValue receiver,
344                                                                           JSTaggedValue key, JSTaggedValue value)
345 {
346     bool isTagged = true;
347     JSTaggedValue originValue = value;
348     auto *hclass = receiver.GetTaggedObject()->GetClass();
349     if (LIKELY(!hclass->IsDictionaryMode())) {
350         ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
351         int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
352         if (entry != -1) {
353             LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
354             PropertyAttributes attr(layoutInfo->GetAttr(entry));
355             ASSERT(static_cast<int>(attr.GetOffset()) == entry);
356             if (UNLIKELY(attr.IsAccessor())) {
357                 if (DefineSemantics(status)) {
358                     return JSTaggedValue::Hole();
359                 }
360                 auto accessor = JSObject::Cast(receiver)->GetProperty(hclass, attr);
361                 if (ShouldCallSetter(receiver, receiver, accessor, attr)) {
362                     return CallSetter(thread, receiver, value, accessor);
363                 }
364             }
365             if (UNLIKELY(!attr.IsWritable())) {
366                 if (DefineSemantics(status)) {
367                     return JSTaggedValue::Hole();
368                 }
369                 [[maybe_unused]] EcmaHandleScope handleScope(thread);
370                 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
371                                             JSTaggedValue::Exception());
372             }
373             if (hclass->IsTS()) {
374                 auto attrVal = JSObject::Cast(receiver)->GetProperty(hclass, attr);
375                 if (attrVal.IsHole()) {
376                     return JSTaggedValue::Hole();
377                 }
378                 JSHandle<JSObject> objHandle(thread, receiver);
379                 JSHandle<JSTaggedValue> valueHandle(thread, value);
380                 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
381                 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
382                     JSHandle<JSTaggedValue>(thread, key), valueHandle, attr);
383                 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
384                 receiver = objHandle.GetTaggedValue();
385                 originValue = valueHandle.GetTaggedValue();
386                 value = actualValue.value;
387                 isTagged = actualValue.isTagged;
388             }
389             if (receiver.IsJSShared()) {
390                 if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), originValue)) {
391                     THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
392                                                 JSTaggedValue::Exception());
393                 }
394             }
395             if (isTagged) {
396                 JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, value);
397             } else {
398                 JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, value);
399             }
400             return JSTaggedValue::Undefined();
401         }
402     }
403     return JSTaggedValue::Hole();
404 }
405 
406 template<ObjectFastOperator::Status status>
SetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,SCheckMode sCheckMode)407 JSTaggedValue ObjectFastOperator::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
408                                                     JSTaggedValue value, SCheckMode sCheckMode)
409 {
410     INTERPRETER_TRACE(thread, SetPropertyByName);
411     // property
412     JSTaggedValue holder = receiver;
413     int receiverHoleEntry = -1;
414     do {
415         auto *hclass = holder.GetTaggedObject()->GetClass();
416         JSType jsType = hclass->GetObjectType();
417         if (IsSpecialIndexedObj(jsType)) {
418             if (IsFastTypeArray(jsType)) {
419                 JSTaggedValue res = FastSetTypeArrayProperty(thread, receiver, holder, key, value, jsType);
420                 if (res.IsNull()) {
421                     return JSTaggedValue::Hole();
422                 } else if (UNLIKELY(!res.IsHole())) {
423                     return res;
424                 }
425             } else if (IsSpecialContainer(jsType)) {
426                 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property on Container", JSTaggedValue::Exception());
427             } else {
428                 return JSTaggedValue::Hole();
429             }
430         }
431         // UpdateRepresentation
432         if (LIKELY(!hclass->IsDictionaryMode())) {
433             ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode());
434             int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
435             if (entry != -1) {
436                 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
437                 PropertyAttributes attr(layoutInfo->GetAttr(entry));
438                 ASSERT(static_cast<int>(attr.GetOffset()) == entry);
439                 if (UNLIKELY(attr.IsAccessor())) {
440                     if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
441                         return JSTaggedValue::Hole();
442                     }
443                     auto accessor = JSObject::Cast(holder)->GetProperty(hclass, attr);
444                     if (ShouldCallSetter(receiver, holder, accessor, attr)) {
445                         return CallSetter(thread, receiver, value, accessor);
446                     }
447                 }
448                 if (UNLIKELY(!attr.IsWritable())) {
449                     if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
450                         return JSTaggedValue::Hole();
451                     }
452                     [[maybe_unused]] EcmaHandleScope handleScope(thread);
453                     THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
454                                                 JSTaggedValue::Exception());
455                 }
456                 if (hclass->IsTS()) {
457                     auto attrVal = JSObject::Cast(holder)->GetProperty(hclass, attr);
458                     if (attrVal.IsHole()) {
459                         if (receiverHoleEntry == -1 && holder == receiver) {
460                             receiverHoleEntry = entry;
461                         }
462                         if (UseOwn(status)) {
463                             break;
464                         }
465                         holder = hclass->GetPrototype();
466                         continue;
467                     }
468                 }
469                 if (UNLIKELY(holder != receiver)) {
470                     break;
471                 }
472                 if (holder.IsJSShared() && (sCheckMode == SCheckMode::CHECK)) {
473                     if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), value)) {
474                         THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
475                                                     JSTaggedValue::Exception());
476                     }
477                 }
478                 JSHandle<JSObject> objHandle(thread, receiver);
479                 ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
480                 auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
481                     JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr);
482                 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
483                 receiver = objHandle.GetTaggedValue();
484                 hclass = objHandle->GetClass();
485                 if (actualValue.isTagged) {
486                     JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, actualValue.value);
487                 } else {
488                     JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, actualValue.value);
489                 }
490                 return JSTaggedValue::Undefined();
491             }
492         } else {
493             TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject());
494             ASSERT(properties->IsDictionaryMode());
495             NameDictionary *dict = NameDictionary::Cast(properties);
496             int entry = dict->FindEntry(key);
497             if (entry != -1) {
498                 auto attr = dict->GetAttributes(entry);
499                 if (UNLIKELY(attr.IsAccessor())) {
500                     if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
501                         return JSTaggedValue::Hole();
502                     }
503                     auto accessor = dict->GetValue(entry);
504                     if (ShouldCallSetter(receiver, holder, accessor, attr)) {
505                         return CallSetter(thread, receiver, value, accessor);
506                     }
507                 }
508                 if (UNLIKELY(!attr.IsWritable())) {
509                     if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
510                         return JSTaggedValue::Hole();
511                     }
512                     [[maybe_unused]] EcmaHandleScope handleScope(thread);
513                     THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
514                                                 JSTaggedValue::Exception());
515                 }
516                 if (UNLIKELY(holder != receiver)) {
517                     break;
518                 }
519                 if ((sCheckMode == SCheckMode::CHECK) && holder.IsJSShared()) {
520                     if (!ClassHelper::MatchFieldType(attr.GetDictSharedFieldType(), value)) {
521                         THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
522                                                     JSTaggedValue::Exception());
523                     }
524                 }
525                 dict->UpdateValue(thread, entry, value);
526                 return JSTaggedValue::Undefined();
527             }
528         }
529         if (UseOwn(status) || DefineSemantics(status)) {
530             break;
531         }
532         holder = hclass->GetPrototype();
533     } while (holder.IsHeapObject());
534 
535     if (receiverHoleEntry != -1) {
536         auto *receiverHClass = receiver.GetTaggedObject()->GetClass();
537         LayoutInfo *receiverLayoutInfo = LayoutInfo::Cast(receiverHClass->GetLayout().GetTaggedObject());
538         PropertyAttributes attr(receiverLayoutInfo->GetAttr(receiverHoleEntry));
539         JSHandle<JSObject> objHandle(thread, receiver);
540         ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
541         auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
542             JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr);
543         JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
544         receiver = objHandle.GetTaggedValue();
545         receiverHClass = objHandle->GetClass();
546         if (actualValue.isTagged) {
547             JSObject::Cast(receiver)->SetProperty<true>(thread, receiverHClass, attr, actualValue.value);
548         } else {
549             JSObject::Cast(receiver)->SetProperty<false>(thread, receiverHClass, attr, actualValue.value);
550         }
551         return JSTaggedValue::Undefined();
552     }
553 
554     [[maybe_unused]] EcmaHandleScope handleScope(thread);
555     JSHandle<JSObject> objHandle(thread, receiver);
556     JSHandle<JSTaggedValue> keyHandle(thread, key);
557     JSHandle<JSTaggedValue> valueHandle(thread, value);
558 
559     if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) {
560         THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible),
561                                     JSTaggedValue::Exception());
562     }
563     ASSERT(!receiver.IsJSShared());
564     PropertyAttributes attr = PropertyAttributes::Default();
565     AddPropertyByName(thread, objHandle, keyHandle, valueHandle, attr);
566     return JSTaggedValue::Undefined();
567 }
568 
569 template <ObjectFastOperator::Status status>
GetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)570 JSTaggedValue ObjectFastOperator::GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
571 {
572     INTERPRETER_TRACE(thread, GetPropertyByIndex);
573     [[maybe_unused]] EcmaHandleScope handleScope(thread);
574     JSTaggedValue holder = receiver;
575     do {
576         auto *hclass = holder.GetTaggedObject()->GetClass();
577         JSType jsType = hclass->GetObjectType();
578         if (IsSpecialIndexedObj(jsType)) {
579             if (jsType == JSType::JS_TYPED_ARRAY) {
580                 return JSTaggedValue::Hole();
581             }
582             if (IsFastTypeArray(jsType)) {
583                 return JSTypedArray::FastGetPropertyByIndex(thread, holder, index, jsType);
584             }
585             if (IsSpecialContainer(jsType)) {
586                 return GetContainerProperty(thread, holder, index, jsType);
587             }
588             if (IsString(jsType)) {
589                 if (index < EcmaStringAccessor(holder).GetLength()) {
590                     EcmaString *subStr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
591                         JSHandle<EcmaString>(thread, holder), index, 1);
592                     return JSTaggedValue(subStr);
593                 }
594             }
595             return JSTaggedValue::Hole();
596         }
597         JSHandle<JSObject> currentHolder(thread, holder);
598         if (!hclass->IsDictionaryElement()) {
599             ASSERT(!ElementAccessor::IsDictionaryMode(currentHolder));
600             if (index < ElementAccessor::GetElementsLength(currentHolder)) {
601                 JSTaggedValue value = ElementAccessor::Get(currentHolder, index);
602                 if (!value.IsHole()) {
603                     return value;
604                 }
605             }
606         } else {
607             TaggedArray *elements = TaggedArray::Cast(currentHolder->GetElements().GetTaggedObject());
608             NumberDictionary *dict = NumberDictionary::Cast(elements);
609             int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index)));
610             if (entry != -1) {
611                 auto attr = dict->GetAttributes(entry);
612                 auto value = dict->GetValue(entry);
613                 if (UNLIKELY(attr.IsAccessor())) {
614                     return CallGetter(thread, receiver, holder, value);
615                 }
616                 ASSERT(!value.IsAccessor());
617                 return value;
618             }
619         }
620         if (UseOwn(status)) {
621             break;
622         }
623         holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
624     } while (holder.IsHeapObject());
625 
626     // not found
627     return JSTaggedValue::Undefined();
628 }
629 
630 template <ObjectFastOperator::Status status>
SetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)631 JSTaggedValue ObjectFastOperator::SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
632                                                      JSTaggedValue value)
633 {
634     INTERPRETER_TRACE(thread, SetPropertyByIndex);
635     JSTaggedValue holder = receiver;
636     do {
637         auto *hclass = holder.GetTaggedObject()->GetClass();
638         JSType jsType = hclass->GetObjectType();
639         if (IsSpecialIndexedObj(jsType)) {
640             if (jsType == JSType::JS_TYPED_ARRAY) {
641                 return JSTaggedValue::Hole();
642             }
643             if (IsFastTypeArray(jsType)) {
644                 CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
645                 return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
646             }
647             if (IsSpecialContainer(jsType)) {
648                 if (DefineSemantics(status)) {
649                     return JSTaggedValue::Hole();
650                 }
651                 return SetContainerProperty(thread, holder, index, value, jsType);
652             }
653             return JSTaggedValue::Hole();
654         }
655         JSHandle<JSObject> arrayHandler(thread, holder);
656         TaggedArray *elements = TaggedArray::Cast(arrayHandler->GetElements().GetTaggedObject());
657         if (!hclass->IsDictionaryElement()) {
658             ASSERT(!elements->IsDictionaryMode());
659             if (UNLIKELY(holder != receiver)) {
660                 break;
661             }
662             if (index < elements->GetLength()) {
663                 JSTaggedValue oldValue = ElementAccessor::Get(arrayHandler, index);
664                 if (!oldValue.IsHole()) {
665                     if (holder.IsJSCOWArray()) {
666                         [[maybe_unused]] EcmaHandleScope handleScope(thread);
667                         JSHandle<JSArray> holderHandler(thread, holder);
668                         JSHandle<JSObject> obj(thread, holder);
669                         JSHandle<JSTaggedValue> valueHandle(thread, value);
670                         // CheckAndCopyArray may cause gc.
671                         JSArray::CheckAndCopyArray(thread, holderHandler);
672                         ElementAccessor::Set(thread, obj, index, valueHandle, true);
673                         return JSTaggedValue::Undefined();
674                     }
675                     JSHandle<JSTaggedValue> valueHandle(thread, value);
676                     ElementAccessor::Set(thread, arrayHandler, index, valueHandle, true);
677                     return JSTaggedValue::Undefined();
678                 }
679             }
680         } else {
681             NumberDictionary *dict = NumberDictionary::Cast(elements);
682             int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index)));
683             if (entry != -1) {
684                 auto attr = dict->GetAttributes(entry);
685                 if (UNLIKELY(!attr.IsWritable() || !attr.IsConfigurable())) {
686                     return JSTaggedValue::Hole();
687                 }
688                 if (UNLIKELY(holder != receiver)) {
689                     break;
690                 }
691                 if (UNLIKELY(attr.IsAccessor())) {
692                     if (DefineSemantics(status)) {
693                         return JSTaggedValue::Hole();
694                     }
695                     auto accessor = dict->GetValue(entry);
696                     if (ShouldCallSetter(receiver, holder, accessor, attr)) {
697                         return CallSetter(thread, receiver, value, accessor);
698                     }
699                 }
700                 dict->UpdateValue(thread, entry, value);
701                 return JSTaggedValue::Undefined();
702             }
703             return JSTaggedValue::Hole();
704         }
705         if (UseOwn(status) || DefineSemantics(status)) {
706             break;
707         }
708         holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
709     } while (holder.IsHeapObject());
710 
711     return AddPropertyByIndex(thread, receiver, index, value);
712 }
713 
714 template <ObjectFastOperator::Status status>
GetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)715 JSTaggedValue ObjectFastOperator::GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
716 {
717     INTERPRETER_TRACE(thread, GetPropertyByValue);
718     if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) {
719         return JSTaggedValue::Hole();
720     }
721     // fast path
722     auto index = TryToElementsIndex(key);
723     if (LIKELY(index >= 0)) {
724         return GetPropertyByIndex<status>(thread, receiver, index);
725     }
726     if (!key.IsNumber()) {
727         if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
728             // update string stable
729             [[maybe_unused]] EcmaHandleScope handleScope(thread);
730             JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
731             key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
732             // Maybe moved by GC
733             receiver = receiverHandler.GetTaggedValue();
734         }
735         return ObjectFastOperator::GetPropertyByName<status>(thread, receiver, key);
736     }
737     return JSTaggedValue::Hole();
738 }
739 
740 template<ObjectFastOperator::Status status>
SetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,SCheckMode sCheckMode)741 JSTaggedValue ObjectFastOperator::SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
742                                                      JSTaggedValue value, SCheckMode sCheckMode)
743 {
744     INTERPRETER_TRACE(thread, SetPropertyByValue);
745     if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) {
746         return JSTaggedValue::Hole();
747     }
748     // fast path
749     auto index = TryToElementsIndex(key);
750     if (LIKELY(index >= 0)) {
751         return SetPropertyByIndex<status>(thread, receiver, index, value);
752     }
753     if (!key.IsNumber()) {
754         if (key.IsString()) {
755             if (!EcmaStringAccessor(key).IsInternString()) {
756                 // update string stable
757                 [[maybe_unused]] EcmaHandleScope handleScope(thread);
758                 JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
759                 JSHandle<JSTaggedValue> valueHandler(thread, value);
760                 key = JSTaggedValue(
761                     thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
762                 // Maybe moved by GC
763                 receiver = receiverHandler.GetTaggedValue();
764                 value = valueHandler.GetTaggedValue();
765             }
766         }
767         ObjectOperator::UpdateDetector(thread, receiver, key);
768         return ObjectFastOperator::SetPropertyByName<status>(thread, receiver, key, value, sCheckMode);
769     }
770     return JSTaggedValue::Hole();
771 }
772 
FastSetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,SCheckMode sCheckMode)773 bool ObjectFastOperator::FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
774                                                 JSTaggedValue value, SCheckMode sCheckMode)
775 {
776     INTERPRETER_TRACE(thread, FastSetPropertyByValue);
777     JSTaggedValue result = ObjectFastOperator::SetPropertyByValue(thread, receiver, key, value, sCheckMode);
778     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
779     if (!result.IsHole()) {
780         return !result.IsException();
781     }
782     return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
783                                       JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value),
784                                       true, sCheckMode);
785 }
786 
FastSetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)787 bool ObjectFastOperator::FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
788                                                 JSTaggedValue value)
789 {
790     INTERPRETER_TRACE(thread, FastSetPropertyByIndex);
791     JSTaggedValue result = ObjectFastOperator::SetPropertyByIndex(thread, receiver, index, value);
792     if (!result.IsHole()) {
793         return !result.IsException();
794     }
795     return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), index,
796                                       JSHandle<JSTaggedValue>(thread, value), true);
797 }
798 
FastGetPropertyByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)799 JSTaggedValue ObjectFastOperator::FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver,
800                                                         JSTaggedValue key)
801 {
802     INTERPRETER_TRACE(thread, FastGetPropertyByName);
803     ASSERT(key.IsStringOrSymbol());
804     if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
805         JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
806         key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
807         // Maybe moved by GC
808         receiver = receiverHandler.GetTaggedValue();
809     }
810     JSTaggedValue result = ObjectFastOperator::GetPropertyByName<Status::GetInternal>(thread, receiver, key);
811     if (result.IsHole()) {
812         return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
813             JSHandle<JSTaggedValue>(thread, key)).GetValue().GetTaggedValue();
814     }
815     return result;
816 }
817 
FastGetPropertyByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,SCheckMode sCheckMode)818 JSTaggedValue ObjectFastOperator::FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
819                                                          SCheckMode sCheckMode)
820 {
821     INTERPRETER_TRACE(thread, FastGetPropertyByValue);
822     JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
823     JSHandle<JSTaggedValue> keyHandler(thread, key);
824     JSTaggedValue result = ObjectFastOperator::GetPropertyByValue(thread, receiver, key);
825     if (result.IsHole()) {
826         return JSTaggedValue::GetProperty(thread, receiverHandler, keyHandler, sCheckMode).GetValue().GetTaggedValue();
827     }
828     return result;
829 }
830 
FastGetPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)831 JSTaggedValue ObjectFastOperator::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
832 {
833     INTERPRETER_TRACE(thread, FastGetPropertyByIndex);
834     JSTaggedValue result = ObjectFastOperator::GetPropertyByIndex(thread, receiver, index);
835     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
836     if (result.IsHole()) {
837         return JSTaggedValue::GetProperty(thread,
838             JSHandle<JSTaggedValue>(thread, receiver), index).GetValue().GetTaggedValue();
839     }
840     return result;
841 }
842 
FastParseDate(const EcmaString * str)843 JSTaggedValue ObjectFastOperator::FastParseDate(const EcmaString *str)
844 {
845     int year = 0;
846     int month = 1;
847     int date = 1;
848     int index = 0;
849 
850     CVector<uint8_t> tmpBuf;
851     EcmaStringAccessor strAccessor(const_cast<EcmaString *>(str));
852     int len = static_cast<int>(strAccessor.GetLength());
853     auto data = reinterpret_cast<const char *>(strAccessor.GetUtf8DataFlat(str, tmpBuf));
854     if (!GetNumFromString(data, len, &index, &year)) {
855         return JSTaggedValue::Hole();
856     }
857     if (!GetNumFromString(data, len, &index, &month)) {
858         return JSTaggedValue::Hole();
859     }
860     if (!GetNumFromString(data, len, &index, &date)) {
861         return JSTaggedValue::Hole();
862     }
863     if (month < 1 || month > JSDate::MONTH_PER_YEAR) {
864         return JSTaggedValue::Hole();
865     }
866     if (date < 1 || date > JSDate::MAX_DAYS_MONTH) {
867         return JSTaggedValue::Hole();
868     }
869     double day = JSDate::MakeDay(year, month - 1, date);
870     double timeValue = JSDate::TimeClip(JSDate::MakeDate(day, 0));
871     return JSTaggedValue(timeValue);
872 }
873 
AddPropertyByName(JSThread * thread,JSHandle<JSObject> objHandle,JSHandle<JSTaggedValue> keyHandle,JSHandle<JSTaggedValue> valueHandle,PropertyAttributes attr)874 PropertyAttributes ObjectFastOperator::AddPropertyByName(JSThread *thread, JSHandle<JSObject> objHandle,
875                                                          JSHandle<JSTaggedValue> keyHandle,
876                                                          JSHandle<JSTaggedValue> valueHandle,
877                                                          PropertyAttributes attr)
878 {
879     INTERPRETER_TRACE(thread, AddPropertyByName);
880 
881     if ((objHandle->IsJSArray() || objHandle->IsTypedArray()) &&
882         keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()) {
883         objHandle->GetJSHClass()->SetHasConstructor(true);
884     }
885     int32_t nextInlinedPropsIndex = objHandle->GetJSHClass()->GetNextInlinedPropsIndex();
886     if (nextInlinedPropsIndex >= 0) {
887         attr.SetOffset(nextInlinedPropsIndex);
888         attr.SetIsInlinedProps(true);
889         attr.SetRepresentation(Representation::TAGGED);
890         auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue());
891         ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
892         JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep);
893         JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
894         oldKind = objHandle->GetJSHClass()->GetElementsKind();
895         auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr);
896         JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
897         if (actualValue.isTagged) {
898             objHandle->SetPropertyInlinedProps<true>(thread, nextInlinedPropsIndex, valueHandle.GetTaggedValue());
899         } else {
900             objHandle->SetPropertyInlinedProps<false>(thread, nextInlinedPropsIndex, actualValue.value);
901         }
902         return attr;
903     }
904 
905     JSMutableHandle<TaggedArray> array(thread, objHandle->GetProperties());
906     uint32_t length = array->GetLength();
907     if (length == 0) {
908         length = JSObject::MIN_PROPERTIES_LENGTH;
909         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
910         array.Update(factory->NewTaggedArray(length).GetTaggedValue());
911         objHandle->SetProperties(thread, array.GetTaggedValue());
912     }
913 
914     if (!array->IsDictionaryMode()) {
915         attr.SetIsInlinedProps(false);
916         uint32_t nonInlinedProps = static_cast<uint32_t>(objHandle->GetJSHClass()->GetNextNonInlinedPropsIndex());
917         ASSERT(length >= nonInlinedProps);
918         uint32_t numberOfProps = objHandle->GetJSHClass()->NumberOfProps();
919         if (UNLIKELY(numberOfProps >= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
920                 // change to dictionary and add one.
921                 JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread, objHandle));
922                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, attr);
923                 JSHandle<NameDictionary> newDict =
924                     NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr);
925                 objHandle->SetProperties(thread, newDict);
926                 // index is not essential when fastMode is false;
927                 return attr;
928         }
929         // if array is full, grow array or change to dictionary mode
930         if (length == nonInlinedProps) {
931             uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity();
932             // Grow properties array size
933             uint32_t capacity = JSObject::ComputeNonInlinedFastPropsCapacity(thread, length,
934                                                                              maxNonInlinedFastPropsCapacity);
935             ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
936             array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue());
937             objHandle->SetProperties(thread, array.GetTaggedValue());
938         }
939 
940         attr.SetOffset(nonInlinedProps + objHandle->GetJSHClass()->GetInlinedProperties());
941         attr.SetRepresentation(Representation::TAGGED);
942         auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue());
943         ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
944         JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep);
945         JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
946         oldKind = objHandle->GetJSHClass()->GetElementsKind();
947         auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr);
948         JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
949         if (actualValue.isTagged) {
950             array->Set<true>(thread, nonInlinedProps, valueHandle.GetTaggedValue());
951         } else {
952             array->Set<false>(thread, nonInlinedProps, actualValue.value);
953         }
954     } else {
955         JSHandle<NameDictionary> dictHandle(array);
956         JSHandle<NameDictionary> newDict =
957             NameDictionary::PutIfAbsent(thread, dictHandle, keyHandle, valueHandle, attr);
958         objHandle->SetProperties(thread, newDict);
959     }
960     return attr;
961 }
962 
CallGetter(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue value)963 JSTaggedValue ObjectFastOperator::CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder,
964                                              JSTaggedValue value)
965 {
966     INTERPRETER_TRACE(thread, CallGetter);
967     // Accessor
968     [[maybe_unused]] EcmaHandleScope handleScope(thread);
969     AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject());
970     if (UNLIKELY(accessor->IsInternal())) {
971         JSHandle<JSObject> objHandle(thread, holder);
972         return accessor->CallInternalGet(thread, objHandle);
973     }
974     JSHandle<JSTaggedValue> objHandle(thread, receiver);
975     return JSObject::CallGetter(thread, accessor, objHandle);
976 }
977 
CallSetter(JSThread * thread,JSTaggedValue receiver,JSTaggedValue value,JSTaggedValue accessorValue)978 JSTaggedValue ObjectFastOperator::CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value,
979                                              JSTaggedValue accessorValue)
980 {
981     INTERPRETER_TRACE(thread, CallSetter);
982     // Accessor
983     [[maybe_unused]] EcmaHandleScope handleScope(thread);
984     JSHandle<JSTaggedValue> objHandle(thread, receiver);
985     JSHandle<JSTaggedValue> valueHandle(thread, value);
986 
987     auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject());
988     bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true);
989     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
990     return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
991 }
992 
ShouldCallSetter(JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue accessorValue,PropertyAttributes attr)993 bool ObjectFastOperator::ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue,
994                                           PropertyAttributes attr)
995 {
996     if (!AccessorData::Cast(accessorValue.GetTaggedObject())->IsInternal()) {
997         return true;
998     }
999     if (receiver != holder) {
1000         return false;
1001     }
1002     return attr.IsWritable();
1003 }
1004 
IsSpecialIndexedObj(JSType jsType)1005 bool ObjectFastOperator::IsSpecialIndexedObj(JSType jsType)
1006 {
1007     return jsType > JSType::JS_ARRAY;
1008 }
1009 
IsJSSharedArray(JSType jsType)1010 bool ObjectFastOperator::IsJSSharedArray(JSType jsType)
1011 {
1012     return jsType == JSType::JS_SHARED_ARRAY;
1013 }
1014 
IsFastTypeArray(JSType jsType)1015 bool ObjectFastOperator::IsFastTypeArray(JSType jsType)
1016 {
1017     return jsType >= JSType::JS_TYPED_ARRAY_FIRST && jsType <= JSType::JS_TYPED_ARRAY_LAST;
1018 }
1019 
IsString(JSType jsType)1020 bool ObjectFastOperator::IsString(JSType jsType)
1021 {
1022     return JSType::STRING_FIRST <= jsType && jsType <= JSType::STRING_LAST;
1023 }
1024 
IsJSPrimitiveRef(JSType jsType)1025 bool ObjectFastOperator::IsJSPrimitiveRef(JSType jsType)
1026 {
1027     return jsType == JSType::JS_PRIMITIVE_REF;
1028 }
1029 
FastGetTypeArrayProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue key,JSType jsType)1030 JSTaggedValue ObjectFastOperator::FastGetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
1031                                                            JSTaggedValue holder,
1032                                                            JSTaggedValue key, JSType jsType)
1033 {
1034     CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
1035     JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
1036     if (UNLIKELY(negativeZero == key)) {
1037         return JSTaggedValue::Undefined();
1038     }
1039     uint32_t index = 0;
1040     if (TryStringOrSymbolToIndex(key, &index)) {
1041         if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
1042             return JSTaggedValue::Null();
1043         }
1044         return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, jsType);
1045     }
1046     return JSTaggedValue::Hole();
1047 }
1048 
TryStringOrSymbolToIndex(JSTaggedValue key,uint32_t * output)1049 bool ObjectFastOperator::TryStringOrSymbolToIndex(JSTaggedValue key, uint32_t *output)
1050 {
1051     if (key.IsSymbol()) {
1052         return false;
1053     }
1054     auto strObj = static_cast<EcmaString *>(key.GetTaggedObject());
1055     return EcmaStringAccessor(strObj).ToTypedArrayIndex(output);
1056 }
1057 
FastSetTypeArrayProperty(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue key,JSTaggedValue value,JSType jsType)1058 JSTaggedValue ObjectFastOperator::FastSetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
1059                                                            JSTaggedValue holder, JSTaggedValue key,
1060                                                            JSTaggedValue value, JSType jsType)
1061 {
1062     CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
1063     JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
1064     if (UNLIKELY(negativeZero == key)) {
1065         if (value.IsECMAObject()) {
1066             return JSTaggedValue::Null();
1067         }
1068         return JSTaggedValue::Undefined();
1069     }
1070     uint32_t index = 0;
1071     if (TryStringOrSymbolToIndex(key, &index)) {
1072         if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
1073             return JSTaggedValue::Null();
1074         }
1075         return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
1076     }
1077     return JSTaggedValue::Hole();
1078 }
1079 
IsSpecialContainer(JSType jsType)1080 bool ObjectFastOperator::IsSpecialContainer(JSType jsType)
1081 {
1082     return jsType >= JSType::JS_API_ARRAY_LIST && jsType <= JSType::JS_API_QUEUE;
1083 }
1084 
GetContainerProperty(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSType jsType)1085 JSTaggedValue ObjectFastOperator::GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1086                                                        JSType jsType)
1087 {
1088     JSTaggedValue res = JSTaggedValue::Undefined();
1089     switch (jsType) {
1090         case JSType::JS_API_ARRAY_LIST:
1091             res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index);
1092             break;
1093         case JSType::JS_API_QUEUE:
1094             res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Get(thread, index);
1095             break;
1096         case JSType::JS_API_PLAIN_ARRAY:
1097             res = JSAPIPlainArray::Cast(receiver.GetTaggedObject())->Get(JSTaggedValue(index));
1098             break;
1099         case JSType::JS_API_DEQUE:
1100             res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Get(index);
1101             break;
1102         case JSType::JS_API_STACK:
1103             res = JSAPIStack::Cast(receiver.GetTaggedObject())->Get(index);
1104             break;
1105         case JSType::JS_API_VECTOR: {
1106             auto self = JSHandle<JSTaggedValue>(thread, receiver);
1107             res = JSAPIVector::Get(thread, JSHandle<JSAPIVector>::Cast(self), index);
1108             break;
1109         }
1110         case JSType::JS_API_LIST: {
1111             res = JSAPIList::Cast(receiver.GetTaggedObject())->Get(index);
1112             break;
1113         }
1114         case JSType::JS_API_BITVECTOR: {
1115             res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Get(thread, index);
1116             break;
1117         }
1118         case JSType::JS_API_LINKED_LIST: {
1119             res = JSAPILinkedList::Cast(receiver.GetTaggedObject())->Get(index);
1120             break;
1121         }
1122         default:
1123             break;
1124     }
1125     return res;
1126 }
1127 
SetContainerProperty(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value,JSType jsType)1128 JSTaggedValue ObjectFastOperator::SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1129                                                        JSTaggedValue value, JSType jsType)
1130 {
1131     JSTaggedValue res = JSTaggedValue::Undefined();
1132     switch (jsType) {
1133         case JSType::JS_API_ARRAY_LIST:
1134             res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1135             break;
1136         case JSType::JS_API_QUEUE:
1137             res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1138             break;
1139         case JSType::JS_API_PLAIN_ARRAY: {
1140             JSHandle<JSAPIPlainArray> plainArray(thread, receiver);
1141             res = JSAPIPlainArray::Set(thread, plainArray, index, value);
1142             break;
1143         }
1144         case JSType::JS_API_DEQUE:
1145             res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1146             break;
1147         case JSType::JS_API_STACK:
1148             res = JSAPIStack::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1149             break;
1150         case JSType::JS_API_VECTOR:
1151             res = JSAPIVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1152             break;
1153         case JSType::JS_API_BITVECTOR:
1154             res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1155             break;
1156         case JSType::JS_API_LIST: {
1157             JSHandle<JSAPIList> singleList(thread, receiver);
1158             res = JSAPIList::Set(thread, singleList, index, JSHandle<JSTaggedValue>(thread, value));
1159             break;
1160         }
1161         case JSType::JS_API_LINKED_LIST: {
1162             JSHandle<JSAPILinkedList> doubleList(thread, receiver);
1163             res = JSAPILinkedList::Set(thread, doubleList, index, JSHandle<JSTaggedValue>(thread, value));
1164             break;
1165         }
1166         default:
1167             break;
1168     }
1169     return res;
1170 }
1171 
AddPropertyByIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index,JSTaggedValue value)1172 JSTaggedValue ObjectFastOperator::AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1173                                                      JSTaggedValue value)
1174 {
1175     INTERPRETER_TRACE(thread, AddPropertyByIndex);
1176     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1177     // fixme(hzzhouzebin) this makes SharedArray's frozen no sense.
1178     if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())  && !receiver.IsJSSharedArray()) {
1179         THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible),
1180                                     JSTaggedValue::Exception());
1181     }
1182 
1183     bool success = JSObject::AddElementInternal(thread, JSHandle<JSObject>(thread, receiver), index,
1184                                                 JSHandle<JSTaggedValue>(thread, value), PropertyAttributes::Default());
1185     return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
1186 }
1187 
TryToElementsIndex(JSTaggedValue key)1188 int64_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key)
1189 {
1190     if (LIKELY(key.IsInt())) {
1191         return key.GetInt();
1192     }
1193     if (key.IsString()) {
1194         uint32_t index = 0;
1195         if (JSTaggedValue::StringToElementIndex(key, &index)) {
1196             return static_cast<int64_t>(index);
1197         }
1198     } else if (key.IsDouble()) {
1199         double number = key.GetDouble();
1200         auto integer = static_cast<int32_t>(number);
1201         if (number == integer) {
1202             return integer;
1203         }
1204     }
1205     return -1;
1206 }
1207 
GetNumFromString(const char * str,int len,int * index,int * num)1208 bool ObjectFastOperator::GetNumFromString(const char *str, int len, int *index, int *num)
1209 {
1210     int indexStr = *index;
1211     char oneByte = 0;
1212     oneByte = str[indexStr];
1213     if (oneByte < '0' || oneByte > '9') {
1214         return false;
1215     }
1216     if (indexStr >= len) {
1217         return false;
1218     }
1219     int value = 0;
1220     while (indexStr < len) {
1221         oneByte = str[indexStr];
1222         int val = static_cast<int>(oneByte - '0');
1223         if (val >= 0 && val <= JSDate::NUM_NINE) {
1224             value = value * JSDate::TEN + val;
1225             indexStr++;
1226         } else if (oneByte != '-') {
1227             return false;
1228         } else {
1229             indexStr++;
1230             break;
1231         }
1232     }
1233     *num = value;
1234     *index = indexStr;
1235     return true;
1236 }
1237 
FastGetPropertyByPorpsIndex(JSThread * thread,JSTaggedValue receiver,uint32_t index)1238 JSTaggedValue ObjectFastOperator::FastGetPropertyByPorpsIndex(JSThread *thread,
1239                                                               JSTaggedValue receiver, uint32_t index)
1240 {
1241     JSTaggedValue value = JSTaggedValue::Hole();
1242     JSObject *obj = JSObject::Cast(receiver);
1243     TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
1244     if (!properties->IsDictionaryMode()) {
1245         JSHClass *jsHclass = obj->GetJSHClass();
1246         LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
1247         PropertyAttributes attr = layoutInfo->GetAttr(index);
1248         value = obj->GetProperty(jsHclass, attr);
1249     } else {
1250         NameDictionary *dict = NameDictionary::Cast(properties);
1251         value = dict->GetValue(index);
1252     }
1253     if (UNLIKELY(value.IsAccessor())) {
1254         return CallGetter(thread, JSTaggedValue(obj), JSTaggedValue(obj), value);
1255     }
1256     ASSERT(!value.IsAccessor());
1257     return value;
1258 }
1259 }
1260 #endif  // ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
1261