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
52namespace 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
59std::pair<JSTaggedValue, bool> ObjectFastOperator::HasOwnProperty(JSThread *thread,
60                                                                  JSTaggedValue receiver, JSTaggedValue key)
61{
62    [[maybe_unused]] DisallowGarbageCollection noGc;
63    if (!receiver.IsHeapObject() || !(receiver.IsRegularObject())) {
64        return std::make_pair(JSTaggedValue::Hole(), false);
65    }
66    if (!key.IsString()) {
67        return std::make_pair(JSTaggedValue::Hole(), false);
68    }
69
70    uint32_t index = 0;
71    if (JSTaggedValue::ToElementIndex(key, &index)) {
72        ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
73        JSHandle<JSObject> receiverObj(thread, receiver);
74        if (ElementAccessor::GetElementsLength(receiverObj) == 0) {
75            return std::make_pair(JSTaggedValue::Hole(), true);  // Empty Array
76        }
77
78        if (!ElementAccessor::IsDictionaryMode(receiverObj)) {
79            if (ElementAccessor::GetElementsLength(receiverObj) <= index) {
80                return std::make_pair(JSTaggedValue::Hole(), true);
81            }
82            JSTaggedValue value = ElementAccessor::Get(receiverObj, index);
83            return std::make_pair(value, true);
84        } else {
85            NumberDictionary *dictionary =
86                NumberDictionary::Cast(JSObject::Cast(receiver)->GetElements().GetTaggedObject());
87            int entry = dictionary->FindEntry(JSTaggedValue(static_cast<int>(index)));
88            if (entry == -1) {
89                return std::make_pair(JSTaggedValue::Hole(), true);
90            }
91            return std::make_pair(JSTaggedValue::Undefined(), true);
92        }
93    }
94
95    if (!EcmaStringAccessor(key).IsInternString()) {
96        JSHandle<EcmaString> keyHandle(thread, key);
97        EcmaString *str = thread->GetEcmaVM()->GetEcmaStringTable()->TryGetInternString(thread, keyHandle);
98        if (str == nullptr) {
99            return std::make_pair(JSTaggedValue::Hole(), true);
100        }
101        key = JSTaggedValue(str);
102    }
103    auto *hclass = receiver.GetTaggedObject()->GetClass();
104    if (LIKELY(!hclass->IsDictionaryMode())) {
105        ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
106        int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
107        if (entry != -1) {
108            return std::make_pair(JSTaggedValue::Undefined(), true);
109        }
110    } else {
111        TaggedArray *array = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject());
112        ASSERT(array->IsDictionaryMode());
113        NameDictionary *dict = NameDictionary::Cast(array);
114        int entry = dict->FindEntry(key);
115        if (entry != -1) {
116            return std::make_pair(JSTaggedValue::Undefined(), true);
117        }
118    }
119    return std::make_pair(JSTaggedValue::Hole(), true);
120}
121
122template <ObjectFastOperator::Status status>
123JSTaggedValue ObjectFastOperator::TryFastHasProperty(JSThread *thread, JSTaggedValue receiver,
124                                                     JSMutableHandle<JSTaggedValue> keyHandle)
125{
126    JSTaggedValue key = keyHandle.GetTaggedValue();
127    if (UNLIKELY(!receiver.IsHeapObject() || !receiver.IsRegularObject())) {
128        return JSTaggedValue::Hole();
129    }
130    if (UNLIKELY(!key.IsNumber() && !key.IsString())) {
131        return JSTaggedValue::Hole();
132    }
133
134    // Elements
135    auto index = TryToElementsIndex(key);
136    if (index >= 0) {
137        ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
138        JSHandle<JSObject> receiverObj(thread, receiver);
139        if (!ElementAccessor::IsDictionaryMode(receiverObj)) {
140            if (index < ElementAccessor::GetElementsLength(receiverObj)) {
141                JSTaggedValue value = ElementAccessor::Get(receiverObj, index);
142                return value.IsHole() ? JSTaggedValue::Hole() : JSTaggedValue::True();
143            }
144        }
145        return JSTaggedValue::Hole();
146    }
147
148    // layout cache
149    auto *hclass = receiver.GetTaggedObject()->GetClass();
150    if (LIKELY(!hclass->IsDictionaryMode())) {
151        if (!EcmaStringAccessor(key).IsInternString()) {
152            JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
153            auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyHandle);
154            EcmaStringAccessor(string).SetInternString();
155            keyHandle.Update(JSTaggedValue(string));
156            // 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
169template <ObjectFastOperator::Status status>
170JSTaggedValue 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
204template<ObjectFastOperator::Status status>
205JSTaggedValue 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(currentHolder, index);
214            if (!value.IsHole()) {
215                return value;
216            }
217        }
218    }
219    return JSTaggedValue::Hole();
220}
221
222template<ObjectFastOperator::Status status>
223JSTaggedValue 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
251template<ObjectFastOperator::Status status>
252JSTaggedValue 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
344template<ObjectFastOperator::Status status>
345JSTaggedValue ObjectFastOperator::TrySetPropertyByNameThroughCacheAtLocal(JSThread *thread, JSTaggedValue receiver,
346                                                                          JSTaggedValue key, JSTaggedValue value)
347{
348    bool isTagged = true;
349    JSTaggedValue originValue = value;
350    auto *hclass = receiver.GetTaggedObject()->GetClass();
351    if (LIKELY(!hclass->IsDictionaryMode())) {
352        ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode());
353        int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
354        if (entry != -1) {
355            LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
356            PropertyAttributes attr(layoutInfo->GetAttr(entry));
357            ASSERT(static_cast<int>(attr.GetOffset()) == entry);
358            if (UNLIKELY(attr.IsAccessor())) {
359                if (DefineSemantics(status)) {
360                    return JSTaggedValue::Hole();
361                }
362                auto accessor = JSObject::Cast(receiver)->GetProperty(hclass, attr);
363                if (ShouldCallSetter(receiver, receiver, accessor, attr)) {
364                    return CallSetter(thread, receiver, value, accessor);
365                }
366            }
367            if (UNLIKELY(!attr.IsWritable())) {
368                if (DefineSemantics(status)) {
369                    return JSTaggedValue::Hole();
370                }
371                [[maybe_unused]] EcmaHandleScope handleScope(thread);
372                THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
373                                            JSTaggedValue::Exception());
374            }
375            if (hclass->IsTS()) {
376                auto attrVal = JSObject::Cast(receiver)->GetProperty(hclass, attr);
377                if (attrVal.IsHole()) {
378                    return JSTaggedValue::Hole();
379                }
380                JSHandle<JSObject> objHandle(thread, receiver);
381                JSHandle<JSTaggedValue> valueHandle(thread, value);
382                ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
383                auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
384                    JSHandle<JSTaggedValue>(thread, key), valueHandle, attr);
385                JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
386                receiver = objHandle.GetTaggedValue();
387                originValue = valueHandle.GetTaggedValue();
388                value = actualValue.value;
389                isTagged = actualValue.isTagged;
390            }
391            if (receiver.IsJSShared()) {
392                if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), originValue)) {
393                    THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
394                                                JSTaggedValue::Exception());
395                }
396            }
397            if (isTagged) {
398                JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, value);
399            } else {
400                JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, value);
401            }
402            return JSTaggedValue::Undefined();
403        }
404    }
405    return JSTaggedValue::Hole();
406}
407
408template<ObjectFastOperator::Status status>
409JSTaggedValue ObjectFastOperator::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
410                                                    JSTaggedValue value, SCheckMode sCheckMode)
411{
412    INTERPRETER_TRACE(thread, SetPropertyByName);
413    // property
414    JSTaggedValue holder = receiver;
415    int receiverHoleEntry = -1;
416    do {
417        auto *hclass = holder.GetTaggedObject()->GetClass();
418        JSType jsType = hclass->GetObjectType();
419        if (IsSpecialIndexedObj(jsType)) {
420            if (IsFastTypeArray(jsType)) {
421                JSTaggedValue res = FastSetTypeArrayProperty(thread, receiver, holder, key, value, jsType);
422                if (res.IsNull()) {
423                    return JSTaggedValue::Hole();
424                } else if (UNLIKELY(!res.IsHole())) {
425                    return res;
426                }
427            } else if (IsSpecialContainer(jsType)) {
428                THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property on Container", JSTaggedValue::Exception());
429            } else {
430                return JSTaggedValue::Hole();
431            }
432        }
433        // UpdateRepresentation
434        if (LIKELY(!hclass->IsDictionaryMode())) {
435            ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode());
436            int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
437            if (entry != -1) {
438                LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
439                PropertyAttributes attr(layoutInfo->GetAttr(entry));
440                ASSERT(static_cast<int>(attr.GetOffset()) == entry);
441                if (UNLIKELY(attr.IsAccessor())) {
442                    if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
443                        return JSTaggedValue::Hole();
444                    }
445                    auto accessor = JSObject::Cast(holder)->GetProperty(hclass, attr);
446                    if (ShouldCallSetter(receiver, holder, accessor, attr)) {
447                        return CallSetter(thread, receiver, value, accessor);
448                    }
449                }
450                if (UNLIKELY(!attr.IsWritable())) {
451                    if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
452                        return JSTaggedValue::Hole();
453                    }
454                    [[maybe_unused]] EcmaHandleScope handleScope(thread);
455                    THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
456                                                JSTaggedValue::Exception());
457                }
458                if (hclass->IsTS()) {
459                    auto attrVal = JSObject::Cast(holder)->GetProperty(hclass, attr);
460                    if (attrVal.IsHole()) {
461                        if (receiverHoleEntry == -1 && holder == receiver) {
462                            receiverHoleEntry = entry;
463                        }
464                        if (UseOwn(status)) {
465                            break;
466                        }
467                        holder = hclass->GetPrototype();
468                        continue;
469                    }
470                }
471                if (UNLIKELY(holder != receiver)) {
472                    break;
473                }
474                if (holder.IsJSShared() && (sCheckMode == SCheckMode::CHECK)) {
475                    if (!ClassHelper::MatchFieldType(attr.GetSharedFieldType(), value)) {
476                        THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
477                                                    JSTaggedValue::Exception());
478                    }
479                }
480                JSHandle<JSObject> objHandle(thread, receiver);
481                ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
482                auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
483                    JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr);
484                JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
485                receiver = objHandle.GetTaggedValue();
486                hclass = objHandle->GetClass();
487                if (actualValue.isTagged) {
488                    JSObject::Cast(receiver)->SetProperty<true>(thread, hclass, attr, actualValue.value);
489                } else {
490                    JSObject::Cast(receiver)->SetProperty<false>(thread, hclass, attr, actualValue.value);
491                }
492                return JSTaggedValue::Undefined();
493            }
494        } else {
495            TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject());
496            ASSERT(properties->IsDictionaryMode());
497            NameDictionary *dict = NameDictionary::Cast(properties);
498            int entry = dict->FindEntry(key);
499            if (entry != -1) {
500                auto attr = dict->GetAttributes(entry);
501                if (UNLIKELY(attr.IsAccessor())) {
502                    if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
503                        return JSTaggedValue::Hole();
504                    }
505                    auto accessor = dict->GetValue(entry);
506                    if (ShouldCallSetter(receiver, holder, accessor, attr)) {
507                        return CallSetter(thread, receiver, value, accessor);
508                    }
509                }
510                if (UNLIKELY(!attr.IsWritable())) {
511                    if (DefineSemantics(status) && sCheckMode == SCheckMode::CHECK) {
512                        return JSTaggedValue::Hole();
513                    }
514                    [[maybe_unused]] EcmaHandleScope handleScope(thread);
515                    THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty),
516                                                JSTaggedValue::Exception());
517                }
518                if (UNLIKELY(holder != receiver)) {
519                    break;
520                }
521                if ((sCheckMode == SCheckMode::CHECK) && holder.IsJSShared()) {
522                    if (!ClassHelper::MatchFieldType(attr.GetDictSharedFieldType(), value)) {
523                        THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
524                                                    JSTaggedValue::Exception());
525                    }
526                }
527                dict->UpdateValue(thread, entry, value);
528                return JSTaggedValue::Undefined();
529            }
530        }
531        if (UseOwn(status) || DefineSemantics(status)) {
532            break;
533        }
534        holder = hclass->GetPrototype();
535    } while (holder.IsHeapObject());
536
537    if (receiverHoleEntry != -1) {
538        auto *receiverHClass = receiver.GetTaggedObject()->GetClass();
539        LayoutInfo *receiverLayoutInfo = LayoutInfo::Cast(receiverHClass->GetLayout().GetTaggedObject());
540        PropertyAttributes attr(receiverLayoutInfo->GetAttr(receiverHoleEntry));
541        JSHandle<JSObject> objHandle(thread, receiver);
542        ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
543        auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle,
544            JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value), attr);
545        JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
546        receiver = objHandle.GetTaggedValue();
547        receiverHClass = objHandle->GetClass();
548        if (actualValue.isTagged) {
549            JSObject::Cast(receiver)->SetProperty<true>(thread, receiverHClass, attr, actualValue.value);
550        } else {
551            JSObject::Cast(receiver)->SetProperty<false>(thread, receiverHClass, attr, actualValue.value);
552        }
553        return JSTaggedValue::Undefined();
554    }
555
556    [[maybe_unused]] EcmaHandleScope handleScope(thread);
557    JSHandle<JSObject> objHandle(thread, receiver);
558    JSHandle<JSTaggedValue> keyHandle(thread, key);
559    JSHandle<JSTaggedValue> valueHandle(thread, value);
560
561    if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) {
562        THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible),
563                                    JSTaggedValue::Exception());
564    }
565    ASSERT(!receiver.IsJSShared());
566    PropertyAttributes attr = PropertyAttributes::Default();
567    AddPropertyByName(thread, objHandle, keyHandle, valueHandle, attr);
568    return JSTaggedValue::Undefined();
569}
570
571template <ObjectFastOperator::Status status>
572JSTaggedValue ObjectFastOperator::GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
573{
574    INTERPRETER_TRACE(thread, GetPropertyByIndex);
575    [[maybe_unused]] EcmaHandleScope handleScope(thread);
576    JSTaggedValue holder = receiver;
577    do {
578        auto *hclass = holder.GetTaggedObject()->GetClass();
579        JSType jsType = hclass->GetObjectType();
580        if (IsSpecialIndexedObj(jsType)) {
581            if (jsType == JSType::JS_TYPED_ARRAY) {
582                return JSTaggedValue::Hole();
583            }
584            if (IsFastTypeArray(jsType)) {
585                return JSTypedArray::FastGetPropertyByIndex(thread, holder, index, jsType);
586            }
587            if (IsSpecialContainer(jsType)) {
588                return GetContainerProperty(thread, holder, index, jsType);
589            }
590            if (IsString(jsType)) {
591                if (index < EcmaStringAccessor(holder).GetLength()) {
592                    EcmaString *subStr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
593                        JSHandle<EcmaString>(thread, holder), index, 1);
594                    return JSTaggedValue(subStr);
595                }
596            }
597            return JSTaggedValue::Hole();
598        }
599        JSHandle<JSObject> currentHolder(thread, holder);
600        if (!hclass->IsDictionaryElement()) {
601            ASSERT(!ElementAccessor::IsDictionaryMode(currentHolder));
602            if (index < ElementAccessor::GetElementsLength(currentHolder)) {
603                JSTaggedValue value = ElementAccessor::Get(currentHolder, index);
604                if (!value.IsHole()) {
605                    return value;
606                }
607            }
608        } else {
609            TaggedArray *elements = TaggedArray::Cast(currentHolder->GetElements().GetTaggedObject());
610            NumberDictionary *dict = NumberDictionary::Cast(elements);
611            int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index)));
612            if (entry != -1) {
613                auto attr = dict->GetAttributes(entry);
614                auto value = dict->GetValue(entry);
615                if (UNLIKELY(attr.IsAccessor())) {
616                    return CallGetter(thread, receiver, holder, value);
617                }
618                ASSERT(!value.IsAccessor());
619                return value;
620            }
621        }
622        if (UseOwn(status)) {
623            break;
624        }
625        holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
626    } while (holder.IsHeapObject());
627
628    // not found
629    return JSTaggedValue::Undefined();
630}
631
632template <ObjectFastOperator::Status status>
633JSTaggedValue ObjectFastOperator::SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
634                                                     JSTaggedValue value)
635{
636    INTERPRETER_TRACE(thread, SetPropertyByIndex);
637    JSTaggedValue holder = receiver;
638    do {
639        auto *hclass = holder.GetTaggedObject()->GetClass();
640        JSType jsType = hclass->GetObjectType();
641        if (IsSpecialIndexedObj(jsType)) {
642            if (jsType == JSType::JS_TYPED_ARRAY) {
643                return JSTaggedValue::Hole();
644            }
645            if (IsFastTypeArray(jsType)) {
646                CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
647                return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
648            }
649            if (IsSpecialContainer(jsType)) {
650                if (DefineSemantics(status)) {
651                    return JSTaggedValue::Hole();
652                }
653                return SetContainerProperty(thread, holder, index, value, jsType);
654            }
655            return JSTaggedValue::Hole();
656        }
657        JSHandle<JSObject> arrayHandler(thread, holder);
658        TaggedArray *elements = TaggedArray::Cast(arrayHandler->GetElements().GetTaggedObject());
659        if (!hclass->IsDictionaryElement()) {
660            ASSERT(!elements->IsDictionaryMode());
661            if (UNLIKELY(holder != receiver)) {
662                break;
663            }
664            if (index < elements->GetLength()) {
665                JSTaggedValue oldValue = ElementAccessor::Get(arrayHandler, index);
666                if (!oldValue.IsHole()) {
667                    if (holder.IsJSCOWArray()) {
668                        [[maybe_unused]] EcmaHandleScope handleScope(thread);
669                        JSHandle<JSArray> holderHandler(thread, holder);
670                        JSHandle<JSObject> obj(thread, holder);
671                        JSHandle<JSTaggedValue> valueHandle(thread, value);
672                        // CheckAndCopyArray may cause gc.
673                        JSArray::CheckAndCopyArray(thread, holderHandler);
674                        ElementAccessor::Set(thread, obj, index, valueHandle, true);
675                        return JSTaggedValue::Undefined();
676                    }
677                    JSHandle<JSTaggedValue> valueHandle(thread, value);
678                    ElementAccessor::Set(thread, arrayHandler, index, valueHandle, true);
679                    return JSTaggedValue::Undefined();
680                }
681            }
682        } else {
683            NumberDictionary *dict = NumberDictionary::Cast(elements);
684            int entry = dict->FindEntry(JSTaggedValue(static_cast<int>(index)));
685            if (entry != -1) {
686                auto attr = dict->GetAttributes(entry);
687                if (UNLIKELY(!attr.IsWritable() || !attr.IsConfigurable())) {
688                    return JSTaggedValue::Hole();
689                }
690                if (UNLIKELY(holder != receiver)) {
691                    break;
692                }
693                if (UNLIKELY(attr.IsAccessor())) {
694                    if (DefineSemantics(status)) {
695                        return JSTaggedValue::Hole();
696                    }
697                    auto accessor = dict->GetValue(entry);
698                    if (ShouldCallSetter(receiver, holder, accessor, attr)) {
699                        return CallSetter(thread, receiver, value, accessor);
700                    }
701                }
702                dict->UpdateValue(thread, entry, value);
703                return JSTaggedValue::Undefined();
704            }
705            return JSTaggedValue::Hole();
706        }
707        if (UseOwn(status) || DefineSemantics(status)) {
708            break;
709        }
710        holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype();
711    } while (holder.IsHeapObject());
712
713    return AddPropertyByIndex(thread, receiver, index, value);
714}
715
716template <ObjectFastOperator::Status status>
717JSTaggedValue ObjectFastOperator::GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
718{
719    INTERPRETER_TRACE(thread, GetPropertyByValue);
720    if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) {
721        return JSTaggedValue::Hole();
722    }
723    // fast path
724    auto index = TryToElementsIndex(key);
725    if (LIKELY(index >= 0)) {
726        return GetPropertyByIndex<status>(thread, receiver, index);
727    }
728    if (!key.IsNumber()) {
729        if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
730            // update string stable
731            [[maybe_unused]] EcmaHandleScope handleScope(thread);
732            JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
733            key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
734            // Maybe moved by GC
735            receiver = receiverHandler.GetTaggedValue();
736        }
737        return ObjectFastOperator::GetPropertyByName<status>(thread, receiver, key);
738    }
739    return JSTaggedValue::Hole();
740}
741
742template<ObjectFastOperator::Status status>
743JSTaggedValue ObjectFastOperator::SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
744                                                     JSTaggedValue value, SCheckMode sCheckMode)
745{
746    INTERPRETER_TRACE(thread, SetPropertyByValue);
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 SetPropertyByIndex<status>(thread, receiver, index, value);
754    }
755    if (!key.IsNumber()) {
756        if (key.IsString()) {
757            if (!EcmaStringAccessor(key).IsInternString()) {
758                // update string stable
759                [[maybe_unused]] EcmaHandleScope handleScope(thread);
760                JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
761                JSHandle<JSTaggedValue> valueHandler(thread, value);
762                key = JSTaggedValue(
763                    thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
764                // Maybe moved by GC
765                receiver = receiverHandler.GetTaggedValue();
766                value = valueHandler.GetTaggedValue();
767            }
768        }
769        ObjectOperator::UpdateDetector(thread, receiver, key);
770        return ObjectFastOperator::SetPropertyByName<status>(thread, receiver, key, value, sCheckMode);
771    }
772    return JSTaggedValue::Hole();
773}
774
775bool ObjectFastOperator::FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
776                                                JSTaggedValue value, SCheckMode sCheckMode)
777{
778    INTERPRETER_TRACE(thread, FastSetPropertyByValue);
779    JSTaggedValue result = ObjectFastOperator::SetPropertyByValue(thread, receiver, key, value, sCheckMode);
780    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
781    if (!result.IsHole()) {
782        return !result.IsException();
783    }
784    return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
785                                      JSHandle<JSTaggedValue>(thread, key), JSHandle<JSTaggedValue>(thread, value),
786                                      true, sCheckMode);
787}
788
789bool ObjectFastOperator::FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
790                                                JSTaggedValue value)
791{
792    INTERPRETER_TRACE(thread, FastSetPropertyByIndex);
793    JSTaggedValue result = ObjectFastOperator::SetPropertyByIndex(thread, receiver, index, value);
794    if (!result.IsHole()) {
795        return !result.IsException();
796    }
797    return JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver), index,
798                                      JSHandle<JSTaggedValue>(thread, value), true);
799}
800
801JSTaggedValue ObjectFastOperator::FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver,
802                                                        JSTaggedValue key)
803{
804    INTERPRETER_TRACE(thread, FastGetPropertyByName);
805    ASSERT(key.IsStringOrSymbol());
806    if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) {
807        JSHandle<JSTaggedValue> receiverHandler(thread, receiver);
808        key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle<JSTaggedValue>(thread, key)));
809        // Maybe moved by GC
810        receiver = receiverHandler.GetTaggedValue();
811    }
812    JSTaggedValue result = ObjectFastOperator::GetPropertyByName<Status::GetInternal>(thread, receiver, key);
813    if (result.IsHole()) {
814        return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
815            JSHandle<JSTaggedValue>(thread, key)).GetValue().GetTaggedValue();
816    }
817    return result;
818}
819
820JSTaggedValue ObjectFastOperator::FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
821                                                         SCheckMode sCheckMode)
822{
823    INTERPRETER_TRACE(thread, FastGetPropertyByValue);
824    JSTaggedValue result = ObjectFastOperator::GetPropertyByValue(thread, receiver, key);
825    if (result.IsHole()) {
826        return JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(thread, receiver),
827            JSHandle<JSTaggedValue>(thread, key), sCheckMode).GetValue().GetTaggedValue();
828    }
829    return result;
830}
831
832JSTaggedValue ObjectFastOperator::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index)
833{
834    INTERPRETER_TRACE(thread, FastGetPropertyByIndex);
835    JSTaggedValue result = ObjectFastOperator::GetPropertyByIndex(thread, receiver, index);
836    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
837    if (result.IsHole()) {
838        return JSTaggedValue::GetProperty(thread,
839            JSHandle<JSTaggedValue>(thread, receiver), index).GetValue().GetTaggedValue();
840    }
841    return result;
842}
843
844JSTaggedValue ObjectFastOperator::FastParseDate(const EcmaString *str)
845{
846    int year = 0;
847    int month = 1;
848    int date = 1;
849    int index = 0;
850
851    CVector<uint8_t> tmpBuf;
852    EcmaStringAccessor strAccessor(const_cast<EcmaString *>(str));
853    int len = static_cast<int>(strAccessor.GetLength());
854    auto data = reinterpret_cast<const char *>(strAccessor.GetUtf8DataFlat(str, tmpBuf));
855    if (!GetNumFromString(data, len, &index, &year)) {
856        return JSTaggedValue::Hole();
857    }
858    if (!GetNumFromString(data, len, &index, &month)) {
859        return JSTaggedValue::Hole();
860    }
861    if (!GetNumFromString(data, len, &index, &date)) {
862        return JSTaggedValue::Hole();
863    }
864    if (month < 1 || month > JSDate::MONTH_PER_YEAR) {
865        return JSTaggedValue::Hole();
866    }
867    if (date < 1 || date > JSDate::MAX_DAYS_MONTH) {
868        return JSTaggedValue::Hole();
869    }
870    double day = JSDate::MakeDay(year, month - 1, date);
871    double timeValue = JSDate::TimeClip(JSDate::MakeDate(day, 0));
872    return JSTaggedValue(timeValue);
873}
874
875PropertyAttributes ObjectFastOperator::AddPropertyByName(JSThread *thread, JSHandle<JSObject> objHandle,
876                                                         JSHandle<JSTaggedValue> keyHandle,
877                                                         JSHandle<JSTaggedValue> valueHandle,
878                                                         PropertyAttributes attr)
879{
880    INTERPRETER_TRACE(thread, AddPropertyByName);
881
882    if ((objHandle->IsJSArray() || objHandle->IsTypedArray()) &&
883        keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()) {
884        objHandle->GetJSHClass()->SetHasConstructor(true);
885    }
886    int32_t nextInlinedPropsIndex = objHandle->GetJSHClass()->GetNextInlinedPropsIndex();
887    if (nextInlinedPropsIndex >= 0) {
888        attr.SetOffset(nextInlinedPropsIndex);
889        attr.SetIsInlinedProps(true);
890        attr.SetRepresentation(Representation::TAGGED);
891        auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue());
892        ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
893        JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep);
894        JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
895        oldKind = objHandle->GetJSHClass()->GetElementsKind();
896        auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr);
897        JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
898        if (actualValue.isTagged) {
899            objHandle->SetPropertyInlinedProps<true>(thread, nextInlinedPropsIndex, valueHandle.GetTaggedValue());
900        } else {
901            objHandle->SetPropertyInlinedProps<false>(thread, nextInlinedPropsIndex, actualValue.value);
902        }
903        return attr;
904    }
905
906    JSMutableHandle<TaggedArray> array(thread, objHandle->GetProperties());
907    uint32_t length = array->GetLength();
908    if (length == 0) {
909        length = JSObject::MIN_PROPERTIES_LENGTH;
910        ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
911        array.Update(factory->NewTaggedArray(length).GetTaggedValue());
912        objHandle->SetProperties(thread, array.GetTaggedValue());
913    }
914
915    if (!array->IsDictionaryMode()) {
916        attr.SetIsInlinedProps(false);
917        uint32_t nonInlinedProps = static_cast<uint32_t>(objHandle->GetJSHClass()->GetNextNonInlinedPropsIndex());
918        ASSERT(length >= nonInlinedProps);
919        uint32_t numberOfProps = objHandle->GetJSHClass()->NumberOfProps();
920        if (UNLIKELY(numberOfProps >= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
921                // change to dictionary and add one.
922                JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread, objHandle));
923                RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, attr);
924                JSHandle<NameDictionary> newDict =
925                    NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr);
926                objHandle->SetProperties(thread, newDict);
927                // index is not essential when fastMode is false;
928                return attr;
929        }
930        // if array is full, grow array or change to dictionary mode
931        if (length == nonInlinedProps) {
932            uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity();
933            // Grow properties array size
934            uint32_t capacity = JSObject::ComputeNonInlinedFastPropsCapacity(thread, length,
935                                                                             maxNonInlinedFastPropsCapacity);
936            ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
937            array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue());
938            objHandle->SetProperties(thread, array.GetTaggedValue());
939        }
940
941        attr.SetOffset(nonInlinedProps + objHandle->GetJSHClass()->GetInlinedProperties());
942        attr.SetRepresentation(Representation::TAGGED);
943        auto rep = PropertyAttributes::TranslateToRep(valueHandle.GetTaggedValue());
944        ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
945        JSHClass::AddProperty(thread, objHandle, keyHandle, attr, rep);
946        JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
947        oldKind = objHandle->GetJSHClass()->GetElementsKind();
948        auto actualValue = JSHClass::ConvertOrTransitionWithRep(thread, objHandle, keyHandle, valueHandle, attr);
949        JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
950        if (actualValue.isTagged) {
951            array->Set<true>(thread, nonInlinedProps, valueHandle.GetTaggedValue());
952        } else {
953            array->Set<false>(thread, nonInlinedProps, actualValue.value);
954        }
955    } else {
956        JSHandle<NameDictionary> dictHandle(array);
957        JSHandle<NameDictionary> newDict =
958            NameDictionary::PutIfAbsent(thread, dictHandle, keyHandle, valueHandle, attr);
959        objHandle->SetProperties(thread, newDict);
960    }
961    return attr;
962}
963
964JSTaggedValue ObjectFastOperator::CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder,
965                                             JSTaggedValue value)
966{
967    INTERPRETER_TRACE(thread, CallGetter);
968    // Accessor
969    [[maybe_unused]] EcmaHandleScope handleScope(thread);
970    AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject());
971    if (UNLIKELY(accessor->IsInternal())) {
972        JSHandle<JSObject> objHandle(thread, holder);
973        return accessor->CallInternalGet(thread, objHandle);
974    }
975    JSHandle<JSTaggedValue> objHandle(thread, receiver);
976    return JSObject::CallGetter(thread, accessor, objHandle);
977}
978
979JSTaggedValue ObjectFastOperator::CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value,
980                                             JSTaggedValue accessorValue)
981{
982    INTERPRETER_TRACE(thread, CallSetter);
983    // Accessor
984    [[maybe_unused]] EcmaHandleScope handleScope(thread);
985    JSHandle<JSTaggedValue> objHandle(thread, receiver);
986    JSHandle<JSTaggedValue> valueHandle(thread, value);
987
988    auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject());
989    bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true);
990    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
991    return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
992}
993
994bool ObjectFastOperator::ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue,
995                                          PropertyAttributes attr)
996{
997    if (!AccessorData::Cast(accessorValue.GetTaggedObject())->IsInternal()) {
998        return true;
999    }
1000    if (receiver != holder) {
1001        return false;
1002    }
1003    return attr.IsWritable();
1004}
1005
1006bool ObjectFastOperator::IsSpecialIndexedObj(JSType jsType)
1007{
1008    return jsType > JSType::JS_ARRAY;
1009}
1010
1011bool ObjectFastOperator::IsJSSharedArray(JSType jsType)
1012{
1013    return jsType == JSType::JS_SHARED_ARRAY;
1014}
1015
1016bool ObjectFastOperator::IsFastTypeArray(JSType jsType)
1017{
1018    return jsType >= JSType::JS_TYPED_ARRAY_FIRST && jsType <= JSType::JS_TYPED_ARRAY_LAST;
1019}
1020
1021bool ObjectFastOperator::IsString(JSType jsType)
1022{
1023    return JSType::STRING_FIRST <= jsType && jsType <= JSType::STRING_LAST;
1024}
1025
1026bool ObjectFastOperator::IsJSPrimitiveRef(JSType jsType)
1027{
1028    return jsType == JSType::JS_PRIMITIVE_REF;
1029}
1030
1031JSTaggedValue ObjectFastOperator::FastGetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
1032                                                           JSTaggedValue holder,
1033                                                           JSTaggedValue key, JSType jsType)
1034{
1035    CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
1036    JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
1037    if (UNLIKELY(negativeZero == key)) {
1038        return JSTaggedValue::Undefined();
1039    }
1040    uint32_t index = 0;
1041    if (TryStringOrSymbolToIndex(key, &index)) {
1042        if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
1043            return JSTaggedValue::Null();
1044        }
1045        return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, jsType);
1046    }
1047    return JSTaggedValue::Hole();
1048}
1049
1050bool ObjectFastOperator::TryStringOrSymbolToIndex(JSTaggedValue key, uint32_t *output)
1051{
1052    if (key.IsSymbol()) {
1053        return false;
1054    }
1055    auto strObj = static_cast<EcmaString *>(key.GetTaggedObject());
1056    return EcmaStringAccessor(strObj).ToTypedArrayIndex(output);
1057}
1058
1059JSTaggedValue ObjectFastOperator::FastSetTypeArrayProperty(JSThread *thread, JSTaggedValue receiver,
1060                                                           JSTaggedValue holder, JSTaggedValue key,
1061                                                           JSTaggedValue value, JSType jsType)
1062{
1063    CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder);
1064    JSTaggedValue negativeZero = thread->GlobalConstants()->GetNegativeZeroString();
1065    if (UNLIKELY(negativeZero == key)) {
1066        if (value.IsECMAObject()) {
1067            return JSTaggedValue::Null();
1068        }
1069        return JSTaggedValue::Undefined();
1070    }
1071    uint32_t index = 0;
1072    if (TryStringOrSymbolToIndex(key, &index)) {
1073        if (UNLIKELY(index == JSObject::MAX_ELEMENT_INDEX)) {
1074            return JSTaggedValue::Null();
1075        }
1076        return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType);
1077    }
1078    return JSTaggedValue::Hole();
1079}
1080
1081bool ObjectFastOperator::IsSpecialContainer(JSType jsType)
1082{
1083    return jsType >= JSType::JS_API_ARRAY_LIST && jsType <= JSType::JS_API_QUEUE;
1084}
1085
1086JSTaggedValue ObjectFastOperator::GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1087                                                       JSType jsType)
1088{
1089    JSTaggedValue res = JSTaggedValue::Undefined();
1090    switch (jsType) {
1091        case JSType::JS_API_ARRAY_LIST:
1092            res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index);
1093            break;
1094        case JSType::JS_API_QUEUE:
1095            res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Get(thread, index);
1096            break;
1097        case JSType::JS_API_PLAIN_ARRAY:
1098            res = JSAPIPlainArray::Cast(receiver.GetTaggedObject())->Get(JSTaggedValue(index));
1099            break;
1100        case JSType::JS_API_DEQUE:
1101            res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Get(index);
1102            break;
1103        case JSType::JS_API_STACK:
1104            res = JSAPIStack::Cast(receiver.GetTaggedObject())->Get(index);
1105            break;
1106        case JSType::JS_API_VECTOR: {
1107            auto self = JSHandle<JSTaggedValue>(thread, receiver);
1108            res = JSAPIVector::Get(thread, JSHandle<JSAPIVector>::Cast(self), index);
1109            break;
1110        }
1111        case JSType::JS_API_LIST: {
1112            res = JSAPIList::Cast(receiver.GetTaggedObject())->Get(index);
1113            break;
1114        }
1115        case JSType::JS_API_BITVECTOR: {
1116            res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Get(thread, index);
1117            break;
1118        }
1119        case JSType::JS_API_LINKED_LIST: {
1120            res = JSAPILinkedList::Cast(receiver.GetTaggedObject())->Get(index);
1121            break;
1122        }
1123        default:
1124            break;
1125    }
1126    return res;
1127}
1128
1129JSTaggedValue ObjectFastOperator::SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1130                                                       JSTaggedValue value, JSType jsType)
1131{
1132    JSTaggedValue res = JSTaggedValue::Undefined();
1133    switch (jsType) {
1134        case JSType::JS_API_ARRAY_LIST:
1135            res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1136            break;
1137        case JSType::JS_API_QUEUE:
1138            res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1139            break;
1140        case JSType::JS_API_PLAIN_ARRAY: {
1141            JSHandle<JSAPIPlainArray> plainArray(thread, receiver);
1142            res = JSAPIPlainArray::Set(thread, plainArray, index, value);
1143            break;
1144        }
1145        case JSType::JS_API_DEQUE:
1146            res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1147            break;
1148        case JSType::JS_API_STACK:
1149            res = JSAPIStack::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1150            break;
1151        case JSType::JS_API_VECTOR:
1152            res = JSAPIVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1153            break;
1154        case JSType::JS_API_BITVECTOR:
1155            res = JSAPIBitVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value);
1156            break;
1157        case JSType::JS_API_LIST: {
1158            JSHandle<JSAPIList> singleList(thread, receiver);
1159            res = JSAPIList::Set(thread, singleList, index, JSHandle<JSTaggedValue>(thread, value));
1160            break;
1161        }
1162        case JSType::JS_API_LINKED_LIST: {
1163            JSHandle<JSAPILinkedList> doubleList(thread, receiver);
1164            res = JSAPILinkedList::Set(thread, doubleList, index, JSHandle<JSTaggedValue>(thread, value));
1165            break;
1166        }
1167        default:
1168            break;
1169    }
1170    return res;
1171}
1172
1173JSTaggedValue ObjectFastOperator::AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index,
1174                                                     JSTaggedValue value)
1175{
1176    INTERPRETER_TRACE(thread, AddPropertyByIndex);
1177    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1178    // fixme(hzzhouzebin) this makes SharedArray's frozen no sense.
1179    if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible()) && !receiver.IsJSSharedArray()) {
1180        THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible),
1181                                    JSTaggedValue::Exception());
1182    }
1183
1184    bool success = JSObject::AddElementInternal(thread, JSHandle<JSObject>(thread, receiver), index,
1185                                                JSHandle<JSTaggedValue>(thread, value), PropertyAttributes::Default());
1186    return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
1187}
1188
1189int64_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key)
1190{
1191    if (LIKELY(key.IsInt())) {
1192        return key.GetInt();
1193    }
1194    if (key.IsString()) {
1195        uint32_t index = 0;
1196        if (JSTaggedValue::StringToElementIndex(key, &index)) {
1197            return static_cast<int64_t>(index);
1198        }
1199    } else if (key.IsDouble()) {
1200        double number = key.GetDouble();
1201        auto integer = static_cast<int32_t>(number);
1202        if (number == integer) {
1203            return integer;
1204        }
1205    }
1206    return -1;
1207}
1208
1209bool ObjectFastOperator::GetNumFromString(const char *str, int len, int *index, int *num)
1210{
1211    int indexStr = *index;
1212    char oneByte = 0;
1213    oneByte = str[indexStr];
1214    if (oneByte < '0' || oneByte > '9') {
1215        return false;
1216    }
1217    if (indexStr >= len) {
1218        return false;
1219    }
1220    int value = 0;
1221    while (indexStr < len) {
1222        oneByte = str[indexStr];
1223        int val = static_cast<int>(oneByte - '0');
1224        if (val >= 0 && val <= JSDate::NUM_NINE) {
1225            value = value * JSDate::TEN + val;
1226            indexStr++;
1227        } else if (oneByte != '-') {
1228            return false;
1229        } else {
1230            indexStr++;
1231            break;
1232        }
1233    }
1234    *num = value;
1235    *index = indexStr;
1236    return true;
1237}
1238
1239JSTaggedValue ObjectFastOperator::FastGetPropertyByPorpsIndex(JSThread *thread,
1240                                                              JSTaggedValue receiver, uint32_t index)
1241{
1242    JSTaggedValue value = JSTaggedValue::Hole();
1243    JSObject *obj = JSObject::Cast(receiver);
1244    TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
1245    if (!properties->IsDictionaryMode()) {
1246        JSHClass *jsHclass = obj->GetJSHClass();
1247        LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
1248        PropertyAttributes attr = layoutInfo->GetAttr(index);
1249        value = obj->GetProperty(jsHclass, attr);
1250    } else {
1251        NameDictionary *dict = NameDictionary::Cast(properties);
1252        value = dict->GetValue(index);
1253    }
1254    if (UNLIKELY(value.IsAccessor())) {
1255        return CallGetter(thread, JSTaggedValue(obj), JSTaggedValue(obj), value);
1256    }
1257    ASSERT(!value.IsAccessor());
1258    return value;
1259}
1260}
1261#endif  // ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H
1262