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