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