1/*
2 * Copyright (c) 2021-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_JSOBJECT_INL_H
17#define ECMASCRIPT_JSOBJECT_INL_H
18
19#include "ecmascript/js_object.h"
20
21#include "ecmascript/element_accessor-inl.h"
22#include "ecmascript/js_array.h"
23#include "ecmascript/js_hclass-inl.h"
24#include "ecmascript/js_tagged_value-inl.h"
25#include "ecmascript/js_typed_array.h"
26#include "ecmascript/object_operator.h"
27#include "ecmascript/tagged_array-inl.h"
28#include "ecmascript/tagged_queue.h"
29#include "ecmascript/tagged_dictionary.h"
30
31namespace panda::ecmascript {
32inline void ECMAObject::SetCallable(bool flag)
33{
34    GetClass()->SetCallable(flag);
35}
36
37inline bool ECMAObject::IsCallable() const
38{
39    return GetClass()->IsCallable();
40}
41
42// JSObject
43inline bool JSObject::IsExtensible() const
44{
45    return GetJSHClass()->IsExtensible();
46}
47
48inline void JSObject::FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end)
49{
50    if (start >= end) {
51        return;
52    }
53
54    JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
55    JSHandle<JSObject> thisObj(thread, this);
56    for (uint32_t i = start; i < end; i++) {
57        ElementAccessor::Set(thread, thisObj, i, holeHandle, false);
58    }
59}
60
61inline uint32_t JSObject::GetNonInlinedFastPropsCapacity() const
62{
63    uint32_t inlineProps = GetJSHClass()->GetInlinedProperties();
64    return PropertyAttributes::MAX_FAST_PROPS_CAPACITY - inlineProps;
65}
66
67inline bool JSObject::IsJSGlobalObject() const
68{
69    return GetJSHClass()->IsJSGlobalObject();
70}
71
72inline bool JSObject::IsConstructor() const
73{
74    return GetJSHClass()->IsConstructor();
75}
76
77inline bool JSObject::IsECMAObject() const
78{
79    return GetJSHClass()->IsECMAObject();
80}
81
82inline bool JSObject::IsJSError() const
83{
84    return GetJSHClass()->IsJSError();
85}
86
87inline bool JSObject::IsArguments() const
88{
89    return GetJSHClass()->IsArguments();
90}
91
92inline bool JSObject::IsDate() const
93{
94    return GetJSHClass()->IsDate();
95}
96
97inline bool JSObject::IsJSArray() const
98{
99    return GetJSHClass()->IsJSArray();
100}
101
102inline bool JSObject::IsJSSArray() const
103{
104    return GetJSHClass()->IsJSSharedArray();
105}
106
107inline bool JSObject::IsJSShared() const
108{
109    return GetJSHClass()->IsJSShared();
110}
111
112inline bool JSObject::IsJSMap() const
113{
114    return GetJSHClass()->IsJSMap();
115}
116
117inline bool JSObject::IsJSSet() const
118{
119    return GetJSHClass()->IsJSSet();
120}
121
122inline bool JSObject::IsJSRegExp() const
123{
124    return GetJSHClass()->IsJSRegExp();
125}
126
127inline bool JSObject::IsJSFunction() const
128{
129    return GetJSHClass()->IsJSFunction();
130}
131
132inline bool JSObject::IsBoundFunction() const
133{
134    return GetJSHClass()->IsJsBoundFunction();
135}
136
137inline bool JSObject::IsJSIntlBoundFunction() const
138{
139    return GetJSHClass()->IsJSIntlBoundFunction();
140}
141
142inline bool JSObject::IsProxyRevocFunction() const
143{
144    return GetJSHClass()->IsJSProxyRevocFunction();
145}
146
147inline bool JSObject::IsAccessorData() const
148{
149    return GetJSHClass()->IsAccessorData();
150}
151
152inline bool JSObject::IsJSGlobalEnv() const
153{
154    return GetJSHClass()->IsJsGlobalEnv();
155}
156
157inline bool JSObject::IsJSProxy() const
158{
159    return GetJSHClass()->IsJSProxy();
160}
161
162inline bool JSObject::IsGeneratorObject() const
163{
164    return GetJSHClass()->IsGeneratorObject();
165}
166
167inline bool JSObject::IsAsyncGeneratorObject() const
168{
169    return GetJSHClass()->IsAsyncGeneratorObject();
170}
171
172inline bool JSObject::IsForinIterator() const
173{
174    return GetJSHClass()->IsForinIterator();
175}
176
177inline bool JSObject::IsJSSetIterator() const
178{
179    return GetJSHClass()->IsJSSetIterator();
180}
181
182inline bool JSObject::IsJSRegExpIterator() const
183{
184    return GetJSHClass()->IsJSRegExpIterator();
185}
186
187inline bool JSObject::IsJSMapIterator() const
188{
189    return GetJSHClass()->IsJSMapIterator();
190}
191
192inline bool JSObject::IsJSArrayIterator() const
193{
194    return GetJSHClass()->IsJSArrayIterator();
195}
196
197inline bool JSObject::IsJSAPIArrayListIterator() const
198{
199    return GetJSHClass()->IsJSAPIArrayListIterator();
200}
201
202inline bool JSObject::IsJSAPIStackIterator() const
203{
204    return GetJSHClass()->IsJSAPIStackIterator();
205}
206
207inline bool JSObject::IsJSAPIVectorIterator() const
208{
209    return GetJSHClass()->IsJSAPIVectorIterator();
210}
211
212inline bool JSObject::IsJSAPIBitVectorIterator() const
213{
214    return GetJSHClass()->IsJSAPIBitVectorIterator();
215}
216
217inline bool JSObject::IsJSAPILinkedListIterator() const
218{
219    return GetJSHClass()->IsJSAPILinkedListIterator();
220}
221
222inline bool JSObject::IsJSAPIListIterator() const
223{
224    return GetJSHClass()->IsJSAPIListIterator();
225}
226
227inline bool JSObject::IsJSPrimitiveRef() const
228{
229    return GetJSHClass()->IsJsPrimitiveRef();
230}
231
232inline bool JSObject::IsElementDict() const
233{
234    return TaggedArray::Cast(GetElements().GetTaggedObject())->IsDictionaryMode();
235}
236
237inline bool JSObject::IsPropertiesDict() const
238{
239    return TaggedArray::Cast(GetProperties().GetTaggedObject())->IsDictionaryMode();
240}
241
242inline bool JSObject::IsTypedArray() const
243{
244    return GetJSHClass()->IsTypedArray();
245}
246
247std::pair<bool, JSTaggedValue> JSObject::ConvertValueWithRep(PropertyAttributes attr, JSTaggedValue value)
248{
249    if (attr.IsDoubleRep()) {
250        if (value.IsInt()) {
251            double doubleValue = value.GetInt();
252            return std::pair(true, JSTaggedValue(bit_cast<JSTaggedType>(doubleValue)));
253        } else if (value.IsDouble()) {
254            return std::pair(true, JSTaggedValue(bit_cast<JSTaggedType>(value.GetDouble())));
255        } else {
256            return std::pair(false, value);
257        }
258    } else if (attr.IsIntRep()) {
259        if (value.IsInt()) {
260            int intValue = value.GetInt();
261            return std::pair(true, JSTaggedValue(static_cast<JSTaggedType>(intValue)));
262        } else {
263            return std::pair(false, value);
264        }
265    }
266    return std::pair(true, value);
267}
268
269void JSObject::SetPropertyInlinedPropsWithRep(const JSThread *thread, uint32_t index, JSTaggedValue value)
270{
271    auto layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject());
272    auto attr = layout->GetAttr(index);
273    if (attr.IsTaggedRep()) {
274        SetPropertyInlinedProps<true>(thread, index, value);
275    } else {
276        SetPropertyInlinedProps<false>(thread, index, value);
277    }
278}
279
280template <bool needBarrier>
281void JSObject::SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value)
282{
283    SetPropertyInlinedProps<needBarrier>(thread, GetJSHClass(), index, value);
284}
285
286JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(uint32_t index, PropertyAttributes attr) const
287{
288    return GetPropertyInlinedPropsWithRep(GetJSHClass(), index, attr);
289}
290
291JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(const JSHClass *hclass, uint32_t index,
292                                                       PropertyAttributes attr) const
293{
294    auto value = GetPropertyInlinedProps(hclass, index);
295    if (attr.IsDoubleRep()) {
296        value = JSTaggedValue(bit_cast<double>(value.GetRawData()));
297    } else if (attr.IsIntRep()) {
298        value = JSTaggedValue(static_cast<int32_t>(value.GetRawData()));
299    }
300    return value;
301}
302
303JSTaggedValue JSObject::GetPropertyInlinedProps(uint32_t index) const
304{
305    return GetPropertyInlinedProps(GetJSHClass(), index);
306}
307
308template <bool needBarrier>
309void JSObject::SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index,
310                                       JSTaggedValue value)
311{
312    uint32_t offset = hclass->GetInlinedPropertiesOffset(index);
313    ASSERT(hclass->GetObjectSize() > offset);
314    if constexpr (needBarrier) {
315        SET_VALUE_WITH_BARRIER(thread, this, offset, value);
316    } else {
317        SET_VALUE_PRIMITIVE(this, offset, value);
318    }
319}
320
321JSTaggedValue JSObject::GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const
322{
323    uint32_t offset = hclass->GetInlinedPropertiesOffset(index);
324    return JSTaggedValue(GET_VALUE(this, offset));
325}
326
327JSTaggedValue JSObject::GetProperty(const JSHClass *hclass, PropertyAttributes attr) const
328{
329    if (attr.IsInlinedProps()) {
330        return GetPropertyInlinedPropsWithRep(hclass, attr.GetOffset(), attr);
331    }
332    TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject());
333    return array->Get(attr.GetOffset() - hclass->GetInlinedProperties());
334}
335
336template <bool needBarrier>
337void JSObject::SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value)
338{
339    if (attr.IsInlinedProps()) {
340        SetPropertyInlinedProps<needBarrier>(thread, hclass, attr.GetOffset(), value);
341    } else {
342        TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject());
343        array->Set<needBarrier>(thread, attr.GetOffset() - hclass->GetInlinedProperties(), value);
344    }
345}
346
347inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index)
348{
349    if (index < capacity) {
350        return false;
351    }
352
353    if (index - capacity > MAX_GAP) {
354        return true;
355    }
356
357    if (index >= static_cast<uint32_t>(INT32_MAX)) {
358        return true;
359    }
360
361    if (capacity >= MIN_GAP) {
362        return index > capacity * FAST_ELEMENTS_FACTOR;
363    }
364
365    return false;
366}
367
368inline bool JSObject::ShouldTransToFastElements(JSThread *thread, TaggedArray *elements,
369                                                uint32_t capacity, uint32_t index)
370{
371    JSHandle<NumberDictionary> dictionary(thread, elements);
372    if (index >= static_cast<uint32_t>(INT32_MAX)) {
373        return false;
374    }
375    // Turn fast if only saves 50% space.
376    if (static_cast<uint32_t>(dictionary->GetLength()) * SHOULD_TRANS_TO_FAST_ELEMENTS_FACTOR >= capacity ||
377        static_cast<uint64_t>(dictionary->NextEnumerationIndex(thread)) >
378        PropertyAttributes::DictionaryOrderField::MaxValue()) {
379        return true;
380    }
381    return false;
382}
383
384inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity, bool isNew)
385{
386    uint32_t newCapacity = isNew ? oldCapacity : (oldCapacity + (oldCapacity >> 1U));
387    return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH;
388}
389
390inline uint32_t JSObject::ComputeElementCapacityHighGrowth(uint32_t oldCapacity)
391{
392    uint32_t newCapacity = oldCapacity * 2;
393    return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH;
394}
395
396inline uint32_t JSObject::ComputeElementCapacityWithHint(uint32_t oldCapacity, uint32_t hint)
397{
398    uint32_t newCapacity = 0;
399    if ((oldCapacity >= hint) || (hint < MIN_ELEMENTS_HINT_LENGTH) || (hint >= MAX_ELEMENTS_HINT_LENGTH)) {
400        return newCapacity;
401    }
402    if ((hint / oldCapacity) <= ELEMENTS_HINT_FACTOR) {
403        newCapacity = hint;
404    }
405    return newCapacity;
406}
407
408inline uint32_t JSObject::ComputeNonInlinedFastPropsCapacity(JSThread *thread, uint32_t oldCapacity,
409                                                             uint32_t maxNonInlinedFastPropsCapacity)
410{
411    uint32_t newCapacity = oldCapacity + thread->GetPropertiesGrowStep();
412    return newCapacity > maxNonInlinedFastPropsCapacity ? maxNonInlinedFastPropsCapacity : newCapacity;
413}
414
415// static
416template<ElementTypes types>
417JSHandle<JSTaggedValue> JSObject::CreateListFromArrayLike(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
418{
419    // 3. If Type(obj) is not Object, throw a TypeError exception.
420    if (!obj->IsECMAObject()) {
421        THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike must accept object",
422                                    JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
423    }
424    if (obj->IsTypedArray()) {
425        uint32_t len = JSHandle<JSTypedArray>::Cast(obj)->GetArrayLength();
426        JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len);
427        JSTypedArray::FastCopyElementToArray(thread, obj, array);
428        // c. ReturnIfAbrupt(next).
429        RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
430        return JSHandle<JSTaggedValue>(array);
431    }
432    // 4. Let len be ToLength(Get(obj, "length")).
433    JSHandle<JSTaggedValue> lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString();
434
435    JSHandle<JSTaggedValue> value = GetProperty(thread, obj, lengthKeyHandle).GetValue();
436    JSTaggedNumber number = JSTaggedValue::ToLength(thread, value);
437    // 5. ReturnIfAbrupt(len).
438    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
439    if (number.GetNumber() > MAX_ELEMENT_INDEX) {
440        THROW_TYPE_ERROR_AND_RETURN(thread, "len is bigger than 2^32 - 1",
441                                    JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
442    }
443
444    uint32_t len = number.ToUint32();
445    // 6. Let list be an empty List.
446    JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len);
447    // 8. Repeat while index < len
448    for (uint32_t i = 0; i < len; i++) {
449        JSTaggedValue next = JSTaggedValue::GetProperty(thread, obj, i).GetValue().GetTaggedValue();
450        // c. ReturnIfAbrupt(next).
451        RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
452
453        if constexpr (types == ElementTypes::STRING_AND_SYMBOL) {
454            if (!next.IsString() && !next.IsSymbol()) {
455                THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike: not an element of elementTypes",
456                                            JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
457            }
458        }
459
460        array->Set(thread, i, next);
461    }
462    return JSHandle<JSTaggedValue>(array);
463}
464
465inline JSTaggedValue JSObject::ShouldGetValueFromBox(ObjectOperator *op)
466{
467    JSTaggedValue result = op->GetValue();
468    if (result.IsPropertyBox()) {
469        result = PropertyBox::Cast(result.GetTaggedObject())->GetValue();
470    }
471    return result;
472}
473
474inline bool JSObject::CheckHClassHit(const JSHandle<JSObject> &obj, const JSHandle<JSHClass> &cls)
475{
476    return obj->GetJSHClass() == *cls;
477}
478
479inline uint32_t JSObject::SetValuesOrEntries(JSThread *thread, const JSHandle<TaggedArray> &prop, uint32_t index,
480                                             const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
481                                             PropertyKind kind)
482{
483    if (kind == PropertyKind::VALUE) {
484        prop->Set(thread, index++, value);
485        return index;
486    }
487    JSHandle<TaggedArray> keyValue = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2);  // 2: key-value pair
488    keyValue->Set(thread, 0, key);
489    keyValue->Set(thread, 1, value);
490    JSHandle<JSArray> entry = JSArray::CreateArrayFromList(thread, keyValue);
491    prop->Set(thread, index++, entry.GetTaggedValue());
492    return index;
493}
494
495inline void JSObject::SetEnumCacheKind(JSThread *thread, TaggedArray *array, EnumCacheKind kind)
496{
497    array->Set(thread, EnumCache::ENUM_CACHE_KIND_OFFSET, JSTaggedValue(static_cast<uint8_t>(kind)));
498}
499
500inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, TaggedArray *array)
501{
502    return static_cast<EnumCacheKind>(array->Get(thread, EnumCache::ENUM_CACHE_KIND_OFFSET).GetInt());
503}
504
505inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, JSTaggedValue enumCache)
506{
507    if (enumCache.IsUndefinedOrNull()) {
508        return EnumCacheKind::NONE;
509    }
510    JSTaggedValue emptyArray = thread->GlobalConstants()->GetEmptyArray();
511    if (enumCache == emptyArray) {
512        return EnumCacheKind::SIMPLE;
513    }
514    TaggedArray *array = TaggedArray::Cast(enumCache.GetTaggedObject());
515    return JSObject::GetEnumCacheKind(thread, array);
516}
517
518inline JSTaggedValue JSObject::GetPrototype(JSTaggedValue obj)
519{
520    JSHClass *hclass = obj.GetTaggedObject()->GetClass();
521    return hclass->GetPrototype();
522}
523
524inline bool JSObject::IsDepulicateKeys(JSThread *thread, JSHandle<TaggedArray> keys, int32_t lastLength,
525                                       JSHandle<TaggedQueue> shadowQueue, JSHandle<JSTaggedValue> key)
526{
527    if (lastLength < 0) {
528        return false;
529    }
530    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
531    for (int32_t i = EnumCache::ENUM_CACHE_HEADER_SIZE; i < lastLength; i++) {
532        value.Update(keys->Get(i));
533        bool has = JSTaggedValue::Equal(thread, value, key);
534        if (has) {
535            return true;
536        }
537    }
538
539    uint32_t shadowSize = shadowQueue->Size();
540    for (uint32_t i = 0; i < shadowSize; i++) {
541        value.Update(shadowQueue->Get(i));
542        bool has = JSTaggedValue::Equal(thread, value, key);
543        if (has) {
544            return true;
545        }
546    }
547    return false;
548}
549
550inline void JSObject::ClearHasDeleteProperty(JSHandle<JSTaggedValue> object)
551{
552    object->GetTaggedObject()->GetClass()->SetHasDeleteProperty(false);
553}
554
555inline std::pair<JSHandle<TaggedArray>, JSHandle<TaggedArray>> JSObject::GetOwnEnumerableNamesInFastMode(
556    JSThread *thread, const JSHandle<JSObject> &obj, uint32_t *copyLengthOfKeys, uint32_t *copyLengthOfElements)
557{
558    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
559    std::pair<uint32_t, uint32_t> numOfKeys = obj->GetNumberOfEnumKeys();
560    uint32_t numOfEnumKeys = numOfKeys.first;
561    uint32_t numOfElements = obj->GetNumberOfElements();
562    JSHandle<TaggedArray> elementArray = numOfElements > 0 ? JSObject::GetEnumElementKeys(
563        thread, obj, 0, numOfElements, copyLengthOfElements) : factory->EmptyArray();
564    JSHandle<TaggedArray> keyArray = numOfEnumKeys > 0 ? JSObject::GetAllEnumKeys(
565        thread, obj, numOfEnumKeys, copyLengthOfKeys) : factory->EmptyArray();
566    return std::make_pair(keyArray, elementArray);
567}
568
569inline bool JSObject::HasMutantTaggedArrayElements(const JSHandle<JSObject> &obj)
570{
571    return obj->GetElements().IsMutantTaggedArray();
572}
573}  //  namespace panda::ecmascript
574#endif  // ECMASCRIPT_JSOBJECT_INL_H
575