/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ECMASCRIPT_JSOBJECT_INL_H #define ECMASCRIPT_JSOBJECT_INL_H #include "ecmascript/js_object.h" #include "ecmascript/element_accessor-inl.h" #include "ecmascript/js_array.h" #include "ecmascript/js_hclass-inl.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/js_typed_array.h" #include "ecmascript/tagged_array-inl.h" #include "ecmascript/tagged_queue.h" #include "ecmascript/tagged_dictionary.h" namespace panda::ecmascript { inline void ECMAObject::SetCallable(bool flag) { GetClass()->SetCallable(flag); } inline bool ECMAObject::IsCallable() const { return GetClass()->IsCallable(); } // JSObject inline bool JSObject::IsExtensible() const { return GetJSHClass()->IsExtensible(); } inline void JSObject::FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end) { if (start >= end) { return; } JSHandle thisObj(thread, this); for (uint32_t i = start; i < end; i++) { ElementAccessor::Set(thread, thisObj, i, JSTaggedValue::Hole(), false); } } inline JSHClass *JSObject::GetJSHClass() const { return GetClass(); } inline uint32_t JSObject::GetNonInlinedFastPropsCapacity() const { uint32_t inlineProps = GetJSHClass()->GetInlinedProperties(); if (inlineProps < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS) { return PropertyAttributes::MAX_FAST_PROPS_CAPACITY - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; } return PropertyAttributes::MAX_FAST_PROPS_CAPACITY - inlineProps; } inline bool JSObject::IsJSGlobalObject() const { return GetJSHClass()->IsJSGlobalObject(); } inline bool JSObject::IsConstructor() const { return GetJSHClass()->IsConstructor(); } inline bool JSObject::IsECMAObject() const { return GetJSHClass()->IsECMAObject(); } inline bool JSObject::IsJSError() const { return GetJSHClass()->IsJSError(); } inline bool JSObject::IsArguments() const { return GetJSHClass()->IsArguments(); } inline bool JSObject::IsDate() const { return GetJSHClass()->IsDate(); } inline bool JSObject::IsJSArray() const { return GetJSHClass()->IsJSArray(); } inline bool JSObject::IsJSMap() const { return GetJSHClass()->IsJSMap(); } inline bool JSObject::IsJSSet() const { return GetJSHClass()->IsJSSet(); } inline bool JSObject::IsJSRegExp() const { return GetJSHClass()->IsJSRegExp(); } inline bool JSObject::IsJSFunction() const { return GetJSHClass()->IsJSFunction(); } inline bool JSObject::IsBoundFunction() const { return GetJSHClass()->IsJsBoundFunction(); } inline bool JSObject::IsJSIntlBoundFunction() const { return GetJSHClass()->IsJSIntlBoundFunction(); } inline bool JSObject::IsProxyRevocFunction() const { return GetJSHClass()->IsJSProxyRevocFunction(); } inline bool JSObject::IsAccessorData() const { return GetJSHClass()->IsAccessorData(); } inline bool JSObject::IsJSGlobalEnv() const { return GetJSHClass()->IsJsGlobalEnv(); } inline bool JSObject::IsJSProxy() const { return GetJSHClass()->IsJSProxy(); } inline bool JSObject::IsGeneratorObject() const { return GetJSHClass()->IsGeneratorObject(); } inline bool JSObject::IsAsyncGeneratorObject() const { return GetJSHClass()->IsAsyncGeneratorObject(); } inline bool JSObject::IsForinIterator() const { return GetJSHClass()->IsForinIterator(); } inline bool JSObject::IsJSSetIterator() const { return GetJSHClass()->IsJSSetIterator(); } inline bool JSObject::IsJSRegExpIterator() const { return GetJSHClass()->IsJSRegExpIterator(); } inline bool JSObject::IsJSMapIterator() const { return GetJSHClass()->IsJSMapIterator(); } inline bool JSObject::IsJSArrayIterator() const { return GetJSHClass()->IsJSArrayIterator(); } inline bool JSObject::IsJSAPIArrayListIterator() const { return GetJSHClass()->IsJSAPIArrayListIterator(); } inline bool JSObject::IsJSAPIStackIterator() const { return GetJSHClass()->IsJSAPIStackIterator(); } inline bool JSObject::IsJSAPIVectorIterator() const { return GetJSHClass()->IsJSAPIVectorIterator(); } inline bool JSObject::IsJSAPILinkedListIterator() const { return GetJSHClass()->IsJSAPILinkedListIterator(); } inline bool JSObject::IsJSAPIListIterator() const { return GetJSHClass()->IsJSAPIListIterator(); } inline bool JSObject::IsJSPrimitiveRef() const { return GetJSHClass()->IsJsPrimitiveRef(); } inline bool JSObject::IsElementDict() const { return TaggedArray::Cast(GetElements().GetTaggedObject())->IsDictionaryMode(); } inline bool JSObject::IsPropertiesDict() const { return TaggedArray::Cast(GetProperties().GetTaggedObject())->IsDictionaryMode(); } inline bool JSObject::IsTypedArray() const { return GetJSHClass()->IsTypedArray(); } std::pair JSObject::ConvertValueWithRep(PropertyAttributes attr, JSTaggedValue value) { if (attr.IsDoubleRep()) { if (value.IsInt()) { double doubleValue = value.GetInt(); return std::pair(true, JSTaggedValue(bit_cast(doubleValue))); } else if (value.IsDouble()) { return std::pair(true, JSTaggedValue(bit_cast(value.GetDouble()))); } else { return std::pair(false, value); } } else if (attr.IsIntRep()) { if (value.IsInt()) { int intValue = value.GetInt(); return std::pair(true, JSTaggedValue(static_cast(intValue))); } else { return std::pair(false, value); } } return std::pair(true, value); } void JSObject::SetPropertyInlinedPropsWithRep(const JSThread *thread, uint32_t index, JSTaggedValue value) { auto layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject()); auto attr = layout->GetAttr(index); if (attr.IsTaggedRep()) { SetPropertyInlinedProps(thread, index, value); } else { SetPropertyInlinedProps(thread, index, value); } } template void JSObject::SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value) { SetPropertyInlinedProps(thread, GetJSHClass(), index, value); } JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(uint32_t index, PropertyAttributes attr) const { return GetPropertyInlinedPropsWithRep(GetJSHClass(), index, attr); } JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(const JSHClass *hclass, uint32_t index, PropertyAttributes attr) const { auto value = GetPropertyInlinedProps(hclass, index); if (attr.IsDoubleRep()) { value = JSTaggedValue(bit_cast(value.GetRawData())); } else if (attr.IsIntRep()) { value = JSTaggedValue(static_cast(value.GetRawData())); } return value; } JSTaggedValue JSObject::GetPropertyInlinedProps(uint32_t index) const { return GetPropertyInlinedProps(GetJSHClass(), index); } template void JSObject::SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, JSTaggedValue value) { uint32_t offset = hclass->GetInlinedPropertiesOffset(index); if (needBarrier) { SET_VALUE_WITH_BARRIER(thread, this, offset, value); } else { SET_VALUE_PRIMITIVE(this, offset, value); } } JSTaggedValue JSObject::GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const { uint32_t offset = hclass->GetInlinedPropertiesOffset(index); return JSTaggedValue(GET_VALUE(this, offset)); } JSTaggedValue JSObject::GetProperty(const JSHClass *hclass, PropertyAttributes attr) const { if (attr.IsInlinedProps()) { return GetPropertyInlinedPropsWithRep(hclass, attr.GetOffset(), attr); } TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); return array->Get(attr.GetOffset() - hclass->GetInlinedProperties()); } template void JSObject::SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value) { if (attr.IsInlinedProps()) { SetPropertyInlinedProps(thread, hclass, attr.GetOffset(), value); } else { TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); array->Set(thread, attr.GetOffset() - hclass->GetInlinedProperties(), value); } } inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index) { if (index < capacity) { return false; } if (index - capacity > MAX_GAP) { return true; } if (index >= static_cast(INT32_MAX)) { return true; } if (capacity >= MIN_GAP) { return index > capacity * FAST_ELEMENTS_FACTOR; } return false; } inline bool JSObject::ShouldTransToFastElements(JSHandle dictionary, uint32_t capacity, uint32_t index) { if (index >= static_cast(INT32_MAX)) { return false; } uint32_t dictionarySize = static_cast(dictionary->GetLength()); // Turn fast if only saves 50% space. if (dictionarySize * SHOULD_TRANS_TO_FAST_ELEMENTS_FACTOR >= capacity) { return true; } return false; } inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity, bool isNew) { uint32_t newCapacity = isNew ? oldCapacity : (oldCapacity + (oldCapacity >> 1U)); return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; } inline uint32_t JSObject::ComputeElementCapacityHighGrowth(uint32_t oldCapacity) { uint32_t newCapacity = oldCapacity * 2; return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; } inline uint32_t JSObject::ComputeElementCapacityWithHint(uint32_t oldCapacity, uint32_t hint) { uint32_t newCapacity = 0; if ((oldCapacity >= hint) || (hint < MIN_ELEMENTS_HINT_LENGTH) || (hint >= MAX_ELEMENTS_HINT_LENGTH)) { return newCapacity; } if ((hint / oldCapacity) <= ELEMENTS_HINT_FACTOR) { newCapacity = hint; } return newCapacity; } inline uint32_t JSObject::ComputeNonInlinedFastPropsCapacity(JSThread *thread, uint32_t oldCapacity, uint32_t maxNonInlinedFastPropsCapacity) { uint32_t newCapacity = oldCapacity + thread->GetPropertiesGrowStep(); return newCapacity > maxNonInlinedFastPropsCapacity ? maxNonInlinedFastPropsCapacity : newCapacity; } // static template JSHandle JSObject::CreateListFromArrayLike(JSThread *thread, const JSHandle &obj) { // 3. If Type(obj) is not Object, throw a TypeError exception. if (!obj->IsECMAObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike must accept object", JSHandle(thread, JSTaggedValue::Exception())); } // 4. Let len be ToLength(Get(obj, "length")). JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); JSHandle value = GetProperty(thread, obj, lengthKeyHandle).GetValue(); JSTaggedNumber number = JSTaggedValue::ToLength(thread, value); // 5. ReturnIfAbrupt(len). RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); if (number.GetNumber() > MAX_ELEMENT_INDEX) { THROW_TYPE_ERROR_AND_RETURN(thread, "len is bigger than 2^32 - 1", JSHandle(thread, JSTaggedValue::Exception())); } uint32_t len = number.ToUint32(); // 6. Let list be an empty List. JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len); if (obj->IsTypedArray()) { JSTypedArray::FastCopyElementToArray(thread, obj, array); // c. ReturnIfAbrupt(next). RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); return JSHandle(array); } // 8. Repeat while index < len for (uint32_t i = 0; i < len; i++) { JSTaggedValue next = JSTaggedValue::GetProperty(thread, obj, i).GetValue().GetTaggedValue(); // c. ReturnIfAbrupt(next). RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); if constexpr (types == ElementTypes::STRING_AND_SYMBOL) { if (!next.IsString() && !next.IsSymbol()) { THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike: not an element of elementTypes", JSHandle(thread, JSTaggedValue::Exception())); } } array->Set(thread, i, next); } return JSHandle(array); } inline JSTaggedValue JSObject::ShouldGetValueFromBox(ObjectOperator *op) { JSTaggedValue result = op->GetValue(); if (result.IsPropertyBox()) { result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(); } return result; } inline bool JSObject::CheckHClassHit(const JSHandle &obj, const JSHandle &cls) { return obj->GetJSHClass() == *cls; } inline uint32_t JSObject::SetValuesOrEntries(JSThread *thread, const JSHandle &prop, uint32_t index, const JSHandle &key, const JSHandle &value, PropertyKind kind) { if (kind == PropertyKind::VALUE) { prop->Set(thread, index++, value); return index; } JSHandle keyValue = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); // 2: key-value pair keyValue->Set(thread, 0, key); keyValue->Set(thread, 1, value); JSHandle entry = JSArray::CreateArrayFromList(thread, keyValue); prop->Set(thread, index++, entry.GetTaggedValue()); return index; } inline void JSObject::SetEnumCacheKind(JSThread *thread, TaggedArray *array, EnumCacheKind kind) { array->Set(thread, EnumCache::ENUM_CACHE_KIND_OFFSET, JSTaggedValue(static_cast(kind))); } inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, TaggedArray *array) { return static_cast(array->Get(thread, EnumCache::ENUM_CACHE_KIND_OFFSET).GetInt()); } inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, JSTaggedValue enumCache) { if (enumCache.IsUndefinedOrNull()) { return EnumCacheKind::NONE; } JSTaggedValue emptyArray = thread->GlobalConstants()->GetEmptyArray(); if (enumCache == emptyArray) { return EnumCacheKind::SIMPLE; } TaggedArray *array = TaggedArray::Cast(enumCache.GetTaggedObject()); return JSObject::GetEnumCacheKind(thread, array); } inline JSTaggedValue JSObject::GetPrototype(JSTaggedValue obj) { JSHClass *hclass = obj.GetTaggedObject()->GetClass(); return hclass->GetPrototype(); } inline bool JSObject::IsDepulicateKeys(JSThread *thread, JSHandle keys, int32_t lastLength, JSHandle shadowQueue, JSHandle key) { if (lastLength < 0) { return false; } JSMutableHandle value(thread, JSTaggedValue::Undefined()); for (int32_t i = EnumCache::ENUM_CACHE_HEADER_SIZE; i < lastLength; i++) { value.Update(keys->Get(i)); bool has = JSTaggedValue::Equal(thread, value, key); if (has) { return true; } } uint32_t shadowSize = shadowQueue->Size(); for (uint32_t i = 0; i < shadowSize; i++) { value.Update(shadowQueue->Get(i)); bool has = JSTaggedValue::Equal(thread, value, key); if (has) { return true; } } return false; } inline void JSObject::ClearHasDeleteProperty(JSHandle object) { object->GetTaggedObject()->GetClass()->SetHasDeleteProperty(false); } inline std::pair, JSHandle> JSObject::GetOwnEnumerableNamesInFastMode( JSThread *thread, const JSHandle &obj, uint32_t *copyLengthOfKeys, uint32_t *copyLengthOfElements) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); std::pair numOfKeys = obj->GetNumberOfEnumKeys(); uint32_t numOfEnumKeys = numOfKeys.first; uint32_t numOfElements = obj->GetNumberOfElements(); JSHandle elementArray = numOfElements > 0 ? JSObject::GetEnumElementKeys( thread, obj, 0, numOfElements, copyLengthOfElements) : factory->EmptyArray(); JSHandle keyArray = numOfEnumKeys > 0 ? JSObject::GetAllEnumKeys( thread, obj, numOfEnumKeys, copyLengthOfKeys) : factory->EmptyArray(); return std::make_pair(keyArray, elementArray); } } // namespace panda::ecmascript #endif // ECMASCRIPT_JSOBJECT_INL_H