From 9373e8b7fcdc6f8f2b136b4f0d21d9bb8e2e5cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BA=91=E9=A3=9E?= Date: Tue, 12 Aug 2025 19:47:48 +0800 Subject: [PATCH] aaa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 杨云飞 --- ecmascript/js_object.cpp | 6001 +++++++++++++++++++------------------- ecmascript/js_proxy.h | 8 +- 2 files changed, 3005 insertions(+), 3004 deletions(-) diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp index e6609c36f6..423d21f706 100644 --- a/ecmascript/js_object.cpp +++ b/ecmascript/js_object.cpp @@ -13,3005 +13,3006 @@ * limitations under the License. */ -#include "ecmascript/js_object.h" -#include "ecmascript/dfx/native_module_failure_info.h" -#include "ecmascript/global_dictionary-inl.h" -#include "ecmascript/ic/proto_change_details.h" -#include "ecmascript/interpreter/interpreter.h" -#include "ecmascript/js_iterator.h" -#include "ecmascript/js_primitive_ref.h" -#include "ecmascript/object_fast_operator-inl.h" -#include "ecmascript/pgo_profiler/pgo_profiler.h" -#include "ecmascript/property_accessor.h" -#include "ecmascript/jspandafile/js_pandafile_manager.h" - -namespace panda::ecmascript { -using PGOProfiler = pgo::PGOProfiler; -PropertyAttributes::PropertyAttributes(const PropertyDescriptor &desc) -{ - DISALLOW_GARBAGE_COLLECTION; - if (desc.HasWritable()) { - SetWritable(desc.IsWritable()); - } - - if (desc.HasEnumerable()) { - SetEnumerable(desc.IsEnumerable()); - } - - if (desc.HasConfigurable()) { - SetConfigurable(desc.IsConfigurable()); - } - - if (desc.IsAccessorDescriptor()) { - SetIsAccessor(true); - } - // internal accessor - if (desc.HasValue() && desc.GetValue()->IsAccessor()) { - SetIsAccessor(true); - } -} - -void ThroughputJSObjectResizingStrategy::UpdateGrowStep(JSThread *thread, uint32_t step) -{ - // 2 : multiple double - thread->SetPropertiesGrowStep(std::min(static_cast(JSObjectResizingStrategy::PROPERTIES_GROW_SIZE * 2), - step)); -} - -Method *ECMAObject::GetCallTarget() const -{ - const TaggedObject *obj = this; - ASSERT(JSTaggedValue(obj).IsJSFunctionBase() || JSTaggedValue(obj).IsJSProxy()); - - JSTaggedValue value; - if (JSTaggedValue(obj).IsJSFunctionBase()) { - value = JSFunctionBase::ConstCast(obj)->GetMethod(); - } else { - value = JSProxy::ConstCast(obj)->GetMethod(); - } - return Method::Cast(value.GetTaggedObject()); -} - -JSHandle JSObject::GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, - uint32_t capacity, bool highGrowth, bool isNew) -{ - uint32_t newCapacity = 0; - if (obj->IsJSArray()) { - uint32_t hint = JSHandle(obj)->GetHintLength(); - newCapacity = ComputeElementCapacityWithHint(capacity, hint); - } - if (obj->IsJSSArray()) { - uint32_t hint = JSHandle(obj)->GetHintLength(); - newCapacity = ComputeElementCapacityWithHint(capacity, hint); - } - if (newCapacity == 0) { - newCapacity = highGrowth ? ComputeElementCapacityHighGrowth(capacity) : - ComputeElementCapacity(capacity, isNew); - } - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle oldElements(thread, obj->GetElements()); - uint32_t oldLength = oldElements->GetLength(); - - ElementsKind kind = obj->GetClass()->GetElementsKind(); - JSHandle newElements = - factory->CopyArray(oldElements, oldLength, newCapacity, JSTaggedValue::Hole(), - obj->IsJSShared() ? MemSpaceType::SHARED_OLD_SPACE : MemSpaceType::SEMI_SPACE, kind); - obj->SetElements(thread, newElements); - if (thread->IsPGOProfilerEnable() && obj->IsJSArray()) { - auto trackInfo = JSHandle(obj)->GetTrackInfo(); - thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackArrayLength(trackInfo, newCapacity); - } - return newElements; -} - -JSHandle JSObject::IterableToList(JSThread *thread, const JSHandle &items, - JSTaggedValue method) -{ - // 1. If method is present, then - // a. Let iteratorRecord be ? GetIterator(items, sync, method). - // 2. Else, - // a. Let iteratorRecord be ? GetIterator(items, sync). - JSHandle iteratorRecord; - JSHandle methodHandle(thread, method); - if (!methodHandle->IsUndefined()) { - iteratorRecord = JSIterator::GetIterator(thread, items, methodHandle); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - } else { - iteratorRecord = JSIterator::GetIterator(thread, items); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - } - // 3. Let values be a new empty List. - // 4. Let next be true. - JSHandle array = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - JSHandle valuesList = JSHandle::Cast(array); - JSMutableHandle next(thread, JSTaggedValue::True()); - // 5. Repeat, while next is not false, - // a. Set next to ? IteratorStep(iteratorRecord). - // b. If next is not false, then - // i. Let nextValue be ? IteratorValue(next). - // ii. Append nextValue to the end of the List values. - uint32_t k = 0; - while (!next->IsFalse()) { - next.Update(JSIterator::IteratorStep(thread, iteratorRecord).GetTaggedValue()); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - if (!next->IsFalse()) { - JSHandle nextValue(JSIterator::IteratorValue(thread, next)); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - JSArray::FastSetPropertyByValue(thread, valuesList, k, nextValue); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - k++; - } - } - // 6. Return values. - return valuesList; -} - -bool JSObject::IsRegExp(JSThread *thread, const JSHandle &argument) -{ - if (!argument->IsECMAObject()) { - return false; - } - JSHandle matchSymbol = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol(); - JSTaggedValue isRegexp = ObjectFastOperator::FastGetPropertyByValue( - thread, argument.GetTaggedValue(), matchSymbol.GetTaggedValue()); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - if (!isRegexp.IsUndefined()) { - return isRegexp.ToBoolean(); - } - JSHandle argumentObj = JSHandle::Cast(argument); - return argumentObj->IsJSRegExp(); -} - -JSHandle JSObject::TransitionToDictionary(const JSThread *thread, const JSHandle &receiver) -{ - JSHandle array(thread, receiver->GetProperties()); - JSHandle jshclass(thread, receiver->GetJSHClass()); - ASSERT(!jshclass->IsDictionaryMode()); - uint32_t propNumber = jshclass->NumberOfProps(); - - ASSERT(!jshclass->GetLayout().IsNull()); - if (jshclass->IsJSShared()) { - THROW_TYPE_ERROR_AND_RETURN(const_cast(thread), - "shared obj does not support changing or deleting attributes", - JSHandle()); - } - JSHandle layoutInfoHandle(thread, jshclass->GetLayout()); - JSMutableHandle dict( - thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propNumber))); - JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); - JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); - uint32_t numberInlinedProps = jshclass->GetInlinedProperties(); - for (uint32_t i = 0; i < propNumber; i++) { - JSTaggedValue key = layoutInfoHandle->GetKey(i); - PropertyAttributes attr = layoutInfoHandle->GetAttr(i); - ASSERT(i == attr.GetOffset()); - JSTaggedValue value; - - if (i < numberInlinedProps) { - value = receiver->GetPropertyInlinedPropsWithRep(i, attr); - // If delete a property in hclass which has subtyping info and not prototype, only set value as hole and - // not remove. When transition to dictionary, exclude it. - if (value.IsHole()) { - continue; - } - } else { - value = array->Get(i - numberInlinedProps); - } - - attr.SetBoxType(PropertyBoxType::UNDEFINED); - valueHandle.Update(value); - keyHandle.Update(key); - JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); - dict.Update(newDict); - } - - receiver->SetProperties(thread, dict); - ElementsKind oldKind = receiver->GetJSHClass()->GetElementsKind(); - // change HClass - JSHClass::TransitionToDictionary(thread, receiver); - JSObject::TryMigrateToGenericKindForJSObject(thread, receiver, oldKind); - - // trim in-obj properties space - TrimInlinePropsSpace(thread, receiver, numberInlinedProps); - - return dict; -} - -void JSObject::ElementsToDictionary(const JSThread *thread, JSHandle obj) -{ - JSHandle elements(thread, obj->GetElements()); - ASSERT(!obj->GetJSHClass()->IsDictionaryElement()); - uint32_t length = elements->GetLength(); - if (obj->IsJSShared()) { - THROW_TYPE_ERROR(const_cast(thread), - "shared obj does not support changing or deleting attributes"); - } - JSMutableHandle dict(thread, NumberDictionary::Create(thread)); - auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes()); - JSMutableHandle key(thread, JSTaggedValue::Undefined()); - JSMutableHandle valueHandle(thread, JSTaggedValue ::Undefined()); - for (uint32_t i = 0; i < length; i++) { - JSTaggedValue value = ElementAccessor::Get(obj, i); - if (value.IsHole()) { - continue; - } - key.Update(JSTaggedValue(i)); - valueHandle.Update(value); - JSHandle newDict = NumberDictionary::PutIfAbsent(thread, dict, key, valueHandle, attr); - dict.Update(newDict); - } - obj->SetElements(thread, dict); - - ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); - JSHClass::TransitionElementsToDictionary(thread, obj); - TryMigrateToGenericKindForJSObject(thread, obj, oldKind); -} - -inline bool JSObject::ShouldOptimizeAsFastElements(const JSThread *thread, JSHandle obj) -{ - JSHandle elements(thread, obj->GetElements()); - uint32_t size = static_cast(elements->Size()); - for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) { - JSTaggedValue key = elements->GetKey(hashIndex); - if (key.IsUndefined() || key.IsHole()) { - continue; - } - PropertyAttributes attr = elements->GetAttributes(hashIndex); - if (!attr.IsDefaultAttributes()) { - return false; - } - } - return true; -} - -void JSObject::TryOptimizeAsFastElements(const JSThread *thread, JSHandle obj) -{ - ASSERT(obj->GetJSHClass()->IsDictionaryElement() && obj->IsJSArray()); - if (ShouldOptimizeAsFastElements(thread, obj)) { - uint32_t length = JSArray::Cast(*obj)->GetLength(); - JSHandle elements(thread, obj->GetElements()); - uint32_t size = static_cast(elements->Size()); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle array = factory->NewTaggedArray(length); - for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) { - JSTaggedValue key = elements->GetKey(hashIndex); - JSTaggedValue value = elements->GetValue(hashIndex); - if (key.IsUndefined() || key.IsHole()) { - continue; - } - ASSERT(key.IsInt()); - uint32_t uintKey = static_cast(key.GetInt()); - if (uintKey < length) { - array->Set(thread, uintKey, value); - } - } - obj->SetElements(thread, array); - JSHClass::OptimizeAsFastElements(thread, obj); - } -} - -void JSObject::OptimizeAsFastProperties(const JSThread *thread, JSHandle obj) -{ - ASSERT(obj->GetJSHClass()->IsDictionaryMode()); - // 1. Get NameDictionary properties - JSHandle properties(thread, obj->GetProperties()); - - int numberOfProperties = properties->EntriesCount(); - // Make sure we preserve enough capacity - if (numberOfProperties > static_cast(PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { - return ; - } - - // 2. iteration indices - std::vector indexOrder = properties->GetEnumerationOrder(); - ASSERT(static_cast(indexOrder.size()) == numberOfProperties); - - // 3. Change Hclass - int numberOfInlinedProps = static_cast(obj->GetJSHClass()->GetInlinedProperties()); - JSHClass::OptimizeAsFastProperties(thread, obj, indexOrder, true); - - // 4. New out-properties - int numberOfOutProperties = numberOfProperties - numberOfInlinedProps; - ASSERT(numberOfOutProperties >= 0); - JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(numberOfOutProperties); - - // 5. Fill properties - for (int i = 0; i < numberOfProperties; i++) { - JSTaggedValue value = properties->GetValue(indexOrder[i]); - if (i < numberOfInlinedProps) { - obj->SetPropertyInlinedPropsWithRep(thread, i, value); - } else { - array->Set(thread, i - numberOfInlinedProps, value); - } - } - obj->SetProperties(thread, array); -} - -void JSObject::SetSProperties(JSThread *thread, JSHandle obj, const std::vector &descs) -{ - uint32_t length = descs.size(); - JSMutableHandle propKey(thread, JSTaggedValue::Undefined()); - JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); - - if (LIKELY(!obj->GetJSHClass()->IsDictionaryMode())) { - for (uint32_t i = 0; i < length; ++i) { - propValue.Update(descs[i].GetValue()); - // note(lzl): IsSAccessor? - if (!propValue->IsSharedType() && !propValue->IsAccessor()) { - THROW_TYPE_ERROR(thread, "The properties of sendable class must be sendable too."); - } - obj->SetPropertyInlinedProps(thread, i, propValue.GetTaggedValue()); - } - } else { - JSMutableHandle dict( - thread, ecmascript::NameDictionary::CreateInSharedHeap( - thread, ecmascript::NameDictionary::ComputeHashTableSize(length))); - for (uint32_t i = 0; i < length; i++) { - propKey.Update(descs[i].GetKey()); - propValue.Update(descs[i].GetValue()); - PropertyAttributes attr = - PropertyAttributes::Default(descs[i].IsWritable(), descs[i].IsEnumerable(), descs[i].IsConfigurable()); - JSHandle newDict = - ecmascript::NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attr); - dict.Update(newDict); - } - obj->SetProperties(thread, dict); - } -} - -bool JSObject::IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver) -{ - auto *hclass = receiver->GetJSHClass(); - if (!hclass->IsDictionaryMode()) { - LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); - PropertyAttributes attr(layoutInfo->GetAttr(JSArray::LENGTH_INLINE_PROPERTY_INDEX)); - return attr.IsWritable(); - } - JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); - ObjectOperator op(thread, receiver, lengthKey, OperatorType::OWN); - return op.GetAttr().IsWritable(); -} - -bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &receiver, - uint32_t index, const JSHandle &value, - PropertyAttributes attr) -{ - ElementsKind kind = ElementsKind::NONE; - if (receiver->IsJSArray()) { - DISALLOW_GARBAGE_COLLECTION; - JSArray *arr = JSArray::Cast(*receiver); - uint32_t oldLength = arr->GetArrayLength(); - if (index >= oldLength) { - if (!IsArrayLengthWritable(thread, receiver)) { - return false; - } - arr->SetArrayLength(thread, index + 1); - if (index > oldLength) { - kind = ElementsKind::HOLE; - } - } - } - if (receiver->IsJSSArray()) { - DISALLOW_GARBAGE_COLLECTION; - JSSharedArray *arr = JSSharedArray::Cast(*receiver); - uint32_t oldLength = arr->GetArrayLength(); - if (index >= oldLength) { - JSHandle newLength(thread, JSTaggedValue(static_cast(index + 1))); - JSSharedArray::LengthSetter(thread, receiver, newLength); - if (index > oldLength) { - kind = ElementsKind::HOLE; - } - } - } - thread->NotifyStableArrayElementsGuardians(receiver, StableArrayChangeKind::NOT_PROTO); - - // check whether to convert to dictionary - if (receiver->GetJSHClass()->IsDictionaryElement() && receiver->IsJSArray()) { - JSArray *arr = JSArray::Cast(*receiver); - uint32_t capacity = arr->GetArrayLength(); - TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); - ASSERT(elements->IsDictionaryMode()); - if (ShouldTransToFastElements(JSHandle(thread, elements), capacity, index)) { - JSObject::TryOptimizeAsFastElements(thread, receiver); - } - } - - bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement(); - TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); - if (isDictionary) { - ASSERT(elements->IsDictionaryMode()); - JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); - JSHandle newDict = - NumberDictionary::Put(thread, JSHandle(thread, elements), keyHandle, value, attr); - receiver->SetElements(thread, newDict); - return true; - } - - uint32_t capacity = elements->GetLength(); - if (index >= capacity || !attr.IsDefaultAttributes()) { - if (!receiver->IsJSSArray() && (ShouldTransToDict(capacity, index) || !attr.IsDefaultAttributes())) { - JSObject::ElementsToDictionary(thread, receiver); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); - JSHandle dict(thread, receiver->GetElements()); - JSHandle newKey = NumberDictionary::Put(thread, dict, keyHandle, value, attr); - receiver->SetElements(thread, newKey); - return true; - } - elements = *JSObject::GrowElementsCapacity(thread, receiver, index + 1); - } - bool needTransition = true; - if (receiver->IsJSShared()) { - needTransition = false; - } - ElementAccessor::Set(thread, receiver, index, value, needTransition, kind); - return true; -} - -void JSObject::DeletePropertyInternal(JSThread *thread, const JSHandle &obj, - const JSHandle &key, uint32_t index) -{ - JSHandle array(thread, obj->GetProperties()); - - if (obj->IsJSGlobalObject()) { - JSHandle dictHandle(thread, obj->GetProperties()); - PropertyBox* box = dictHandle->GetBox(index); - box->Clear(thread); - JSHandle newDict = GlobalDictionary::Remove(thread, dictHandle, index); - obj->SetProperties(thread, newDict); - return; - } - - if (!array->IsDictionaryMode()) { - JSHandle dictHandle(TransitionToDictionary(thread, obj)); - RETURN_IF_ABRUPT_COMPLETION(thread); - int entry = dictHandle->FindEntry(key.GetTaggedValue()); - ASSERT(entry != -1); - JSHandle newDict = NameDictionary::Remove(thread, dictHandle, entry); - obj->SetProperties(thread, newDict); - return; - } - - JSHandle dictHandle(array); - JSHandle newDict = NameDictionary::Remove(thread, dictHandle, index); - obj->SetProperties(thread, newDict); -} - -void JSObject::GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, - const JSHandle &keyArray) - -{ - TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); - if (!array->IsDictionaryMode()) { - int end = static_cast(obj->GetJSHClass()->NumberOfProps()); - if (end > 0) { - LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject()) - ->GetAllKeys(thread, end, offset, *keyArray, obj); - } - return; - } - - if (obj->IsJSGlobalObject()) { - GlobalDictionary *dict = GlobalDictionary::Cast(array); - return dict->GetAllKeys(thread, offset, *keyArray); - } - - NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetAllKeys(thread, offset, *keyArray); -} - -void JSObject::GetAllKeysByFilter(const JSThread *thread, const JSHandle &obj, - uint32_t &keyArrayEffectivelength, - const JSHandle &keyArray, - uint32_t filter) -{ - TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); - if (!array->IsDictionaryMode()) { - uint32_t numberOfProps = obj->GetJSHClass()->NumberOfProps(); - if (numberOfProps > 0) { - LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())-> - GetAllKeysByFilter(thread, numberOfProps, keyArrayEffectivelength, *keyArray, obj, filter); - } - return; - } - - if (obj->IsJSGlobalObject()) { - GlobalDictionary *dict = GlobalDictionary::Cast(array); - return dict->GetAllKeysByFilter(thread, keyArrayEffectivelength, *keyArray, filter); - } - - NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetAllKeysByFilter(thread, keyArrayEffectivelength, *keyArray, filter); -} - -// For Serialization use. Does not support JSGlobalObject -void JSObject::GetAllKeysForSerialization(const JSHandle &obj, std::vector &keyVector) -{ - DISALLOW_GARBAGE_COLLECTION; - ASSERT_PRINT(!obj->IsJSGlobalObject(), "Do not support get key of JSGlobal Object"); - TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); - if (!array->IsDictionaryMode()) { - int end = static_cast(obj->GetJSHClass()->NumberOfProps()); - if (end > 0) { - LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())->GetAllKeysForSerialization(end, - keyVector); - } - } else { - NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetAllKeysIntoVector(keyVector); - } -} - -JSHandle JSObject::GetAllEnumKeys(JSThread *thread, const JSHandle &obj, - uint32_t numOfKeys, uint32_t *keys) -{ - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - if (obj->IsJSGlobalObject()) { - JSHandle keyArray = factory->NewTaggedArray(numOfKeys); - GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetEnumAllKeys(thread, 0, *keyArray, keys); - return keyArray; - } - - TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); - if (!array->IsDictionaryMode()) { - JSHClass *jsHclass = obj->GetJSHClass(); - JSTaggedValue enumCache = jsHclass->GetEnumCache(); - if (JSObject::GetEnumCacheKind(thread, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) { - JSHandle cacheArray = JSHandle(thread, enumCache); - JSHandle keyArray = factory->CopyFromEnumCache(cacheArray); - *keys = keyArray->GetLength(); - return keyArray; - } - - if (numOfKeys > 0) { - int end = static_cast(jsHclass->NumberOfProps()); - JSHandle keyArray = factory->NewTaggedArray(numOfKeys + EnumCache::ENUM_CACHE_HEADER_SIZE); - LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()) - ->GetAllEnumKeys(thread, end, EnumCache::ENUM_CACHE_HEADER_SIZE, keyArray, keys, obj); - JSObject::SetEnumCacheKind(thread, *keyArray, EnumCacheKind::ONLY_OWN_KEYS); - if (!JSTaggedValue(jsHclass).IsInSharedHeap()) { - jsHclass->SetEnumCache(thread, keyArray.GetTaggedValue()); - } - JSHandle newkeyArray = factory->CopyFromEnumCache(keyArray); - return newkeyArray; - } - return factory->EmptyArray(); - } - - JSHandle keyArray = factory->NewTaggedArray(numOfKeys); - NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetAllEnumKeys(thread, 0, keyArray, keys); - return keyArray; -} - -uint32_t JSObject::GetAllEnumKeys(JSThread *thread, const JSHandle &obj, int offset, - const JSHandle &keyArray) -{ - TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); - uint32_t keys = 0; - if (!array->IsDictionaryMode()) { - JSHClass *jsHclass = obj->GetJSHClass(); - int end = static_cast(jsHclass->NumberOfProps()); - if (end > 0) { - LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()) - ->GetAllEnumKeys(thread, end, offset, keyArray, &keys, obj); - } - return keys; - } - if (obj->IsJSGlobalObject()) { - GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetEnumAllKeys(thread, offset, *keyArray, &keys); - return keys; - } - - NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetAllEnumKeys(thread, offset, keyArray, &keys); - return keys; -} - -void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, - const JSHandle &keyArray) -{ - uint32_t elementIndex = 0; - if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { - elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + static_cast(offset); - for (uint32_t i = static_cast(offset); i < elementIndex; ++i) { - auto key = base::NumberHelper::IntToEcmaString(thread, i); - keyArray->Set(thread, i, key); - } - } - - if (!ElementAccessor::IsDictionaryMode(obj)) { - uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); - for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { - if (!ElementAccessor::Get(obj, i).IsHole()) { - auto key = base::NumberHelper::IntToEcmaString(thread, i); - keyArray->Set(thread, j++, key); - } - } - } else { - JSHandle elements(thread, obj->GetElements()); - NumberDictionary::GetAllKeys(thread, JSHandle(elements), elementIndex, keyArray); - } -} - -void JSObject::GetAllElementKeysByFilter(JSThread *thread, - const JSHandle &obj, - const JSHandle &keyArray, - uint32_t &keyArrayEffectiveLength, - uint32_t filter) -{ - ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); - uint32_t elementIndex = 0; - - // For strings attributes, only enumerable is true - if ((filter & NATIVE_ENUMERABLE) && obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { - elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength(); - for (uint32_t i = 0; i < elementIndex; ++i) { - keyArray->Set(thread, keyArrayEffectiveLength, JSTaggedValue(i)); - keyArrayEffectiveLength++; - } - } - - JSHandle objValue(obj); - - if (!ElementAccessor::IsDictionaryMode(obj)) { - uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); - for (uint32_t i = 0; i < elementsLen; ++i) { - if (!ElementAccessor::Get(obj, i).IsHole()) { - ObjectOperator op(thread, objValue, i, OperatorType::OWN); - bool bIgnore = FilterHelper::IgnoreKeyByFilter(op, filter); - if (bIgnore) { - continue; - } - keyArray->Set(thread, keyArrayEffectiveLength, JSTaggedValue(i)); - keyArrayEffectiveLength++; - } - } - } else { - JSHandle elements(thread, obj->GetElements()); - NumberDictionary::GetAllKeysByFilter(thread, JSHandle(elements), - keyArrayEffectiveLength, keyArray, filter); - } -} - -void JSObject::GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, - std::vector &keyVector) -{ - if (!ElementAccessor::IsDictionaryMode(obj)) { - uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); - for (uint32_t i = 0; i < elementsLen; ++i) { - if (!ElementAccessor::Get(obj, i).IsHole()) { - keyVector.emplace_back(JSTaggedValue(i)); - } - } - } else { - JSHandle elements(thread, obj->GetElements()); - JSHandle dict = JSHandle::Cast(elements); - dict->GetAllKeysIntoVector(keyVector); - } -} - -JSHandle JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, - uint32_t numOfElements, uint32_t *keys) -{ - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle elementArray = factory->NewTaggedArray(numOfElements); - CollectEnumElementsAlongProtoChain(thread, obj, offset, elementArray, keys); - return elementArray; -} - -void JSObject::CollectEnumElementsAlongProtoChain(JSThread *thread, const JSHandle &obj, int offset, - JSHandle elementArray, uint32_t *keys, - int32_t lastLength) -{ - uint32_t elementIndex = static_cast(offset); - JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); - - if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { - uint32_t strLen = JSPrimitiveRef::Cast(*obj)->GetStringLength(); - for (uint32_t i = 0; i < strLen; ++i) { - keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i)); - elementArray->Set(thread, elementIndex, keyHandle); - elementIndex++; - } - *keys += strLen; - } - - if (!ElementAccessor::IsDictionaryMode(obj)) { - JSHandle emptyQueue = thread->GetEcmaVM()->GetFactory()->GetEmptyTaggedQueue(); - uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); - uint32_t preElementIndex = elementIndex; - for (uint32_t i = 0; i < elementsLen; ++i) { - if (ElementAccessor::Get(obj, i).IsHole()) { - continue; - } - keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i)); - bool isDuplicated = IsDepulicateKeys(thread, elementArray, lastLength, emptyQueue, keyHandle); - if (isDuplicated) { - continue; - } - elementArray->Set(thread, elementIndex, keyHandle); - elementIndex++; - } - *keys += (elementIndex - preElementIndex); - } else { - JSHandle arr(thread, obj->GetElements()); - NumberDictionary::GetAllEnumKeys( - thread, JSHandle(arr), elementIndex, elementArray, keys, lastLength); - } -} - -void JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, + #include "ecmascript/js_object.h" + #include "ecmascript/dfx/native_module_failure_info.h" + #include "ecmascript/global_dictionary-inl.h" + #include "ecmascript/ic/proto_change_details.h" + #include "ecmascript/interpreter/interpreter.h" + #include "ecmascript/js_iterator.h" + #include "ecmascript/js_primitive_ref.h" + #include "ecmascript/object_fast_operator-inl.h" + #include "ecmascript/pgo_profiler/pgo_profiler.h" + #include "ecmascript/property_accessor.h" + #include "ecmascript/jspandafile/js_pandafile_manager.h" + + namespace panda::ecmascript { + using PGOProfiler = pgo::PGOProfiler; + PropertyAttributes::PropertyAttributes(const PropertyDescriptor &desc) + { + DISALLOW_GARBAGE_COLLECTION; + if (desc.HasWritable()) { + SetWritable(desc.IsWritable()); + } + + if (desc.HasEnumerable()) { + SetEnumerable(desc.IsEnumerable()); + } + + if (desc.HasConfigurable()) { + SetConfigurable(desc.IsConfigurable()); + } + + if (desc.IsAccessorDescriptor()) { + SetIsAccessor(true); + } + // internal accessor + if (desc.HasValue() && desc.GetValue()->IsAccessor()) { + SetIsAccessor(true); + } + } + + void ThroughputJSObjectResizingStrategy::UpdateGrowStep(JSThread *thread, uint32_t step) + { + // 2 : multiple double + thread->SetPropertiesGrowStep(std::min(static_cast(JSObjectResizingStrategy::PROPERTIES_GROW_SIZE * 2), + step)); + } + + Method *ECMAObject::GetCallTarget() const + { + const TaggedObject *obj = this; + ASSERT(JSTaggedValue(obj).IsJSFunctionBase() || JSTaggedValue(obj).IsJSProxy()); + + JSTaggedValue value; + if (JSTaggedValue(obj).IsJSFunctionBase()) { + value = JSFunctionBase::ConstCast(obj)->GetMethod(); + } else { + value = JSProxy::ConstCast(obj)->GetMethod(); + } + return Method::Cast(value.GetTaggedObject()); + } + + JSHandle JSObject::GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity, bool highGrowth, bool isNew) + { + uint32_t newCapacity = 0; + if (obj->IsJSArray()) { + uint32_t hint = JSHandle(obj)->GetHintLength(); + newCapacity = ComputeElementCapacityWithHint(capacity, hint); + } + if (obj->IsJSSArray()) { + uint32_t hint = JSHandle(obj)->GetHintLength(); + newCapacity = ComputeElementCapacityWithHint(capacity, hint); + } + if (newCapacity == 0) { + newCapacity = highGrowth ? ComputeElementCapacityHighGrowth(capacity) : + ComputeElementCapacity(capacity, isNew); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle oldElements(thread, obj->GetElements()); + uint32_t oldLength = oldElements->GetLength(); + + ElementsKind kind = obj->GetClass()->GetElementsKind(); + JSHandle newElements = + factory->CopyArray(oldElements, oldLength, newCapacity, JSTaggedValue::Hole(), + obj->IsJSShared() ? MemSpaceType::SHARED_OLD_SPACE : MemSpaceType::SEMI_SPACE, kind); + obj->SetElements(thread, newElements); + if (thread->IsPGOProfilerEnable() && obj->IsJSArray()) { + auto trackInfo = JSHandle(obj)->GetTrackInfo(); + thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackArrayLength(trackInfo, newCapacity); + } + return newElements; + } + + JSHandle JSObject::IterableToList(JSThread *thread, const JSHandle &items, + JSTaggedValue method) + { + // 1. If method is present, then + // a. Let iteratorRecord be ? GetIterator(items, sync, method). + // 2. Else, + // a. Let iteratorRecord be ? GetIterator(items, sync). + JSHandle iteratorRecord; + JSHandle methodHandle(thread, method); + if (!methodHandle->IsUndefined()) { + iteratorRecord = JSIterator::GetIterator(thread, items, methodHandle); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + } else { + iteratorRecord = JSIterator::GetIterator(thread, items); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + } + // 3. Let values be a new empty List. + // 4. Let next be true. + JSHandle array = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + JSHandle valuesList = JSHandle::Cast(array); + JSMutableHandle next(thread, JSTaggedValue::True()); + // 5. Repeat, while next is not false, + // a. Set next to ? IteratorStep(iteratorRecord). + // b. If next is not false, then + // i. Let nextValue be ? IteratorValue(next). + // ii. Append nextValue to the end of the List values. + uint32_t k = 0; + while (!next->IsFalse()) { + next.Update(JSIterator::IteratorStep(thread, iteratorRecord).GetTaggedValue()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + if (!next->IsFalse()) { + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + JSArray::FastSetPropertyByValue(thread, valuesList, k, nextValue); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + k++; + } + } + // 6. Return values. + return valuesList; + } + + bool JSObject::IsRegExp(JSThread *thread, const JSHandle &argument) + { + if (!argument->IsECMAObject()) { + return false; + } + JSHandle matchSymbol = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol(); + JSTaggedValue isRegexp = ObjectFastOperator::FastGetPropertyByValue( + thread, argument.GetTaggedValue(), matchSymbol.GetTaggedValue()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!isRegexp.IsUndefined()) { + return isRegexp.ToBoolean(); + } + JSHandle argumentObj = JSHandle::Cast(argument); + return argumentObj->IsJSRegExp(); + } + + JSHandle JSObject::TransitionToDictionary(const JSThread *thread, const JSHandle &receiver) + { + JSHandle array(thread, receiver->GetProperties()); + JSHandle jshclass(thread, receiver->GetJSHClass()); + ASSERT(!jshclass->IsDictionaryMode()); + uint32_t propNumber = jshclass->NumberOfProps(); + + ASSERT(!jshclass->GetLayout().IsNull()); + if (jshclass->IsJSShared()) { + THROW_TYPE_ERROR_AND_RETURN(const_cast(thread), + "shared obj does not support changing or deleting attributes", + JSHandle()); + } + JSHandle layoutInfoHandle(thread, jshclass->GetLayout()); + JSMutableHandle dict( + thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propNumber))); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + uint32_t numberInlinedProps = jshclass->GetInlinedProperties(); + for (uint32_t i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfoHandle->GetKey(i); + PropertyAttributes attr = layoutInfoHandle->GetAttr(i); + ASSERT(i == attr.GetOffset()); + JSTaggedValue value; + + if (i < numberInlinedProps) { + value = receiver->GetPropertyInlinedPropsWithRep(i, attr); + // If delete a property in hclass which has subtyping info and not prototype, only set value as hole and + // not remove. When transition to dictionary, exclude it. + if (value.IsHole()) { + continue; + } + } else { + value = array->Get(i - numberInlinedProps); + } + + attr.SetBoxType(PropertyBoxType::UNDEFINED); + valueHandle.Update(value); + keyHandle.Update(key); + JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); + dict.Update(newDict); + } + + receiver->SetProperties(thread, dict); + ElementsKind oldKind = receiver->GetJSHClass()->GetElementsKind(); + // change HClass + JSHClass::TransitionToDictionary(thread, receiver); + JSObject::TryMigrateToGenericKindForJSObject(thread, receiver, oldKind); + + // trim in-obj properties space + TrimInlinePropsSpace(thread, receiver, numberInlinedProps); + + return dict; + } + + void JSObject::ElementsToDictionary(const JSThread *thread, JSHandle obj) + { + JSHandle elements(thread, obj->GetElements()); + ASSERT(!obj->GetJSHClass()->IsDictionaryElement()); + uint32_t length = elements->GetLength(); + if (obj->IsJSShared()) { + THROW_TYPE_ERROR(const_cast(thread), + "shared obj does not support changing or deleting attributes"); + } + JSMutableHandle dict(thread, NumberDictionary::Create(thread)); + auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes()); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread, JSTaggedValue ::Undefined()); + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue value = ElementAccessor::Get(obj, i); + if (value.IsHole()) { + continue; + } + key.Update(JSTaggedValue(i)); + valueHandle.Update(value); + JSHandle newDict = NumberDictionary::PutIfAbsent(thread, dict, key, valueHandle, attr); + dict.Update(newDict); + } + obj->SetElements(thread, dict); + + ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); + JSHClass::TransitionElementsToDictionary(thread, obj); + TryMigrateToGenericKindForJSObject(thread, obj, oldKind); + } + + inline bool JSObject::ShouldOptimizeAsFastElements(const JSThread *thread, JSHandle obj) + { + JSHandle elements(thread, obj->GetElements()); + uint32_t size = static_cast(elements->Size()); + for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = elements->GetKey(hashIndex); + if (key.IsUndefined() || key.IsHole()) { + continue; + } + PropertyAttributes attr = elements->GetAttributes(hashIndex); + if (!attr.IsDefaultAttributes()) { + return false; + } + } + return true; + } + + void JSObject::TryOptimizeAsFastElements(const JSThread *thread, JSHandle obj) + { + ASSERT(obj->GetJSHClass()->IsDictionaryElement() && obj->IsJSArray()); + if (ShouldOptimizeAsFastElements(thread, obj)) { + uint32_t length = JSArray::Cast(*obj)->GetLength(); + JSHandle elements(thread, obj->GetElements()); + uint32_t size = static_cast(elements->Size()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array = factory->NewTaggedArray(length); + for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = elements->GetKey(hashIndex); + JSTaggedValue value = elements->GetValue(hashIndex); + if (key.IsUndefined() || key.IsHole()) { + continue; + } + ASSERT(key.IsInt()); + uint32_t uintKey = static_cast(key.GetInt()); + if (uintKey < length) { + array->Set(thread, uintKey, value); + } + } + obj->SetElements(thread, array); + JSHClass::OptimizeAsFastElements(thread, obj); + } + } + + void JSObject::OptimizeAsFastProperties(const JSThread *thread, JSHandle obj) + { + ASSERT(obj->GetJSHClass()->IsDictionaryMode()); + // 1. Get NameDictionary properties + JSHandle properties(thread, obj->GetProperties()); + + int numberOfProperties = properties->EntriesCount(); + // Make sure we preserve enough capacity + if (numberOfProperties > static_cast(PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { + return ; + } + + // 2. iteration indices + std::vector indexOrder = properties->GetEnumerationOrder(); + ASSERT(static_cast(indexOrder.size()) == numberOfProperties); + + // 3. Change Hclass + int numberOfInlinedProps = static_cast(obj->GetJSHClass()->GetInlinedProperties()); + JSHClass::OptimizeAsFastProperties(thread, obj, indexOrder, true); + + // 4. New out-properties + int numberOfOutProperties = numberOfProperties - numberOfInlinedProps; + ASSERT(numberOfOutProperties >= 0); + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(numberOfOutProperties); + + // 5. Fill properties + for (int i = 0; i < numberOfProperties; i++) { + JSTaggedValue value = properties->GetValue(indexOrder[i]); + if (i < numberOfInlinedProps) { + obj->SetPropertyInlinedPropsWithRep(thread, i, value); + } else { + array->Set(thread, i - numberOfInlinedProps, value); + } + } + obj->SetProperties(thread, array); + } + + void JSObject::SetSProperties(JSThread *thread, JSHandle obj, const std::vector &descs) + { + uint32_t length = descs.size(); + JSMutableHandle propKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); + + if (LIKELY(!obj->GetJSHClass()->IsDictionaryMode())) { + for (uint32_t i = 0; i < length; ++i) { + propValue.Update(descs[i].GetValue()); + // note(lzl): IsSAccessor? + if (!propValue->IsSharedType() && !propValue->IsAccessor()) { + THROW_TYPE_ERROR(thread, "The properties of sendable class must be sendable too."); + } + obj->SetPropertyInlinedProps(thread, i, propValue.GetTaggedValue()); + } + } else { + JSMutableHandle dict( + thread, ecmascript::NameDictionary::CreateInSharedHeap( + thread, ecmascript::NameDictionary::ComputeHashTableSize(length))); + for (uint32_t i = 0; i < length; i++) { + propKey.Update(descs[i].GetKey()); + propValue.Update(descs[i].GetValue()); + PropertyAttributes attr = + PropertyAttributes::Default(descs[i].IsWritable(), descs[i].IsEnumerable(), descs[i].IsConfigurable()); + JSHandle newDict = + ecmascript::NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attr); + dict.Update(newDict); + } + obj->SetProperties(thread, dict); + } + } + + bool JSObject::IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver) + { + auto *hclass = receiver->GetJSHClass(); + if (!hclass->IsDictionaryMode()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + PropertyAttributes attr(layoutInfo->GetAttr(JSArray::LENGTH_INLINE_PROPERTY_INDEX)); + return attr.IsWritable(); + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + ObjectOperator op(thread, receiver, lengthKey, OperatorType::OWN); + return op.GetAttr().IsWritable(); + } + + bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &receiver, + uint32_t index, const JSHandle &value, + PropertyAttributes attr) + { + ElementsKind kind = ElementsKind::NONE; + if (receiver->IsJSArray()) { + DISALLOW_GARBAGE_COLLECTION; + JSArray *arr = JSArray::Cast(*receiver); + uint32_t oldLength = arr->GetArrayLength(); + if (index >= oldLength) { + if (!IsArrayLengthWritable(thread, receiver)) { + return false; + } + arr->SetArrayLength(thread, index + 1); + if (index > oldLength) { + kind = ElementsKind::HOLE; + } + } + } + if (receiver->IsJSSArray()) { + DISALLOW_GARBAGE_COLLECTION; + JSSharedArray *arr = JSSharedArray::Cast(*receiver); + uint32_t oldLength = arr->GetArrayLength(); + if (index >= oldLength) { + JSHandle newLength(thread, JSTaggedValue(static_cast(index + 1))); + JSSharedArray::LengthSetter(thread, receiver, newLength); + if (index > oldLength) { + kind = ElementsKind::HOLE; + } + } + } + thread->NotifyStableArrayElementsGuardians(receiver, StableArrayChangeKind::NOT_PROTO); + + // check whether to convert to dictionary + if (receiver->GetJSHClass()->IsDictionaryElement() && receiver->IsJSArray()) { + JSArray *arr = JSArray::Cast(*receiver); + uint32_t capacity = arr->GetArrayLength(); + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + ASSERT(elements->IsDictionaryMode()); + if (ShouldTransToFastElements(JSHandle(thread, elements), capacity, index)) { + JSObject::TryOptimizeAsFastElements(thread, receiver); + } + } + + bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement(); + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (isDictionary) { + ASSERT(elements->IsDictionaryMode()); + JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); + JSHandle newDict = + NumberDictionary::Put(thread, JSHandle(thread, elements), keyHandle, value, attr); + receiver->SetElements(thread, newDict); + return true; + } + + uint32_t capacity = elements->GetLength(); + if (index >= capacity || !attr.IsDefaultAttributes()) { + if (!receiver->IsJSSArray() && (ShouldTransToDict(capacity, index) || !attr.IsDefaultAttributes())) { + JSObject::ElementsToDictionary(thread, receiver); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); + JSHandle dict(thread, receiver->GetElements()); + JSHandle newKey = NumberDictionary::Put(thread, dict, keyHandle, value, attr); + receiver->SetElements(thread, newKey); + return true; + } + elements = *JSObject::GrowElementsCapacity(thread, receiver, index + 1); + } + bool needTransition = true; + if (receiver->IsJSShared()) { + needTransition = false; + } + ElementAccessor::Set(thread, receiver, index, value, needTransition, kind); + return true; + } + + void JSObject::DeletePropertyInternal(JSThread *thread, const JSHandle &obj, + const JSHandle &key, uint32_t index) + { + JSHandle array(thread, obj->GetProperties()); + + if (obj->IsJSGlobalObject()) { + JSHandle dictHandle(thread, obj->GetProperties()); + PropertyBox* box = dictHandle->GetBox(index); + box->Clear(thread); + JSHandle newDict = GlobalDictionary::Remove(thread, dictHandle, index); + obj->SetProperties(thread, newDict); + return; + } + + if (!array->IsDictionaryMode()) { + JSHandle dictHandle(TransitionToDictionary(thread, obj)); + RETURN_IF_ABRUPT_COMPLETION(thread); + int entry = dictHandle->FindEntry(key.GetTaggedValue()); + ASSERT(entry != -1); + JSHandle newDict = NameDictionary::Remove(thread, dictHandle, entry); + obj->SetProperties(thread, newDict); + return; + } + + JSHandle dictHandle(array); + JSHandle newDict = NameDictionary::Remove(thread, dictHandle, index); + obj->SetProperties(thread, newDict); + } + + void JSObject::GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) + + { + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + int end = static_cast(obj->GetJSHClass()->NumberOfProps()); + if (end > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject()) + ->GetAllKeys(thread, end, offset, *keyArray, obj); + } + return; + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(array); + return dict->GetAllKeys(thread, offset, *keyArray); + } + + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllKeys(thread, offset, *keyArray); + } + + void JSObject::GetAllKeysByFilter(const JSThread *thread, const JSHandle &obj, + uint32_t &keyArrayEffectivelength, + const JSHandle &keyArray, + uint32_t filter) + { + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + uint32_t numberOfProps = obj->GetJSHClass()->NumberOfProps(); + if (numberOfProps > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())-> + GetAllKeysByFilter(thread, numberOfProps, keyArrayEffectivelength, *keyArray, obj, filter); + } + return; + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(array); + return dict->GetAllKeysByFilter(thread, keyArrayEffectivelength, *keyArray, filter); + } + + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllKeysByFilter(thread, keyArrayEffectivelength, *keyArray, filter); + } + + // For Serialization use. Does not support JSGlobalObject + void JSObject::GetAllKeysForSerialization(const JSHandle &obj, std::vector &keyVector) + { + DISALLOW_GARBAGE_COLLECTION; + ASSERT_PRINT(!obj->IsJSGlobalObject(), "Do not support get key of JSGlobal Object"); + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + int end = static_cast(obj->GetJSHClass()->NumberOfProps()); + if (end > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())->GetAllKeysForSerialization(end, + keyVector); + } + } else { + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllKeysIntoVector(keyVector); + } + } + + JSHandle JSObject::GetAllEnumKeys(JSThread *thread, const JSHandle &obj, + uint32_t numOfKeys, uint32_t *keys) + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (obj->IsJSGlobalObject()) { + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetEnumAllKeys(thread, 0, *keyArray, keys); + return keyArray; + } + + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + JSHClass *jsHclass = obj->GetJSHClass(); + JSTaggedValue enumCache = jsHclass->GetEnumCache(); + if (JSObject::GetEnumCacheKind(thread, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) { + JSHandle cacheArray = JSHandle(thread, enumCache); + JSHandle keyArray = factory->CopyFromEnumCache(cacheArray); + *keys = keyArray->GetLength(); + return keyArray; + } + + if (numOfKeys > 0) { + int end = static_cast(jsHclass->NumberOfProps()); + JSHandle keyArray = factory->NewTaggedArray(numOfKeys + EnumCache::ENUM_CACHE_HEADER_SIZE); + LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()) + ->GetAllEnumKeys(thread, end, EnumCache::ENUM_CACHE_HEADER_SIZE, keyArray, keys, obj); + JSObject::SetEnumCacheKind(thread, *keyArray, EnumCacheKind::ONLY_OWN_KEYS); + if (!JSTaggedValue(jsHclass).IsInSharedHeap()) { + jsHclass->SetEnumCache(thread, keyArray.GetTaggedValue()); + } + JSHandle newkeyArray = factory->CopyFromEnumCache(keyArray); + return newkeyArray; + } + return factory->EmptyArray(); + } + + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllEnumKeys(thread, 0, keyArray, keys); + return keyArray; + } + + uint32_t JSObject::GetAllEnumKeys(JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) + { + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + uint32_t keys = 0; + if (!array->IsDictionaryMode()) { + JSHClass *jsHclass = obj->GetJSHClass(); + int end = static_cast(jsHclass->NumberOfProps()); + if (end > 0) { + LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()) + ->GetAllEnumKeys(thread, end, offset, keyArray, &keys, obj); + } + return keys; + } + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetEnumAllKeys(thread, offset, *keyArray, &keys); + return keys; + } + + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllEnumKeys(thread, offset, keyArray, &keys); + return keys; + } + + void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, const JSHandle &keyArray) -{ - uint32_t elementIndex = 0; - if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { - elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + static_cast(offset); - for (uint32_t i = static_cast(offset); i < elementIndex; ++i) { - auto key = base::NumberHelper::IntToEcmaString(thread, i); - keyArray->Set(thread, i, key); - } - } - - if (!ElementAccessor::IsDictionaryMode(obj)) { - uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); - for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { - if (!ElementAccessor::Get(obj, i).IsHole()) { - auto key = base::NumberHelper::IntToEcmaString(thread, i); - keyArray->Set(thread, j++, key); - } - } - } else { - JSHandle elements(thread, obj->GetElements()); - uint32_t keys = 0; - NumberDictionary::GetAllEnumKeys(thread, JSHandle(elements), elementIndex, keyArray, &keys); - } -} - -std::pair JSObject::GetNumberOfEnumKeys() const -{ - DISALLOW_GARBAGE_COLLECTION; - TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); - if (!array->IsDictionaryMode()) { - int end = static_cast(GetJSHClass()->NumberOfProps()); - if (end > 0) { - LayoutInfo *layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject()); - return layout->GetNumOfEnumKeys(end, this); - } - return std::make_pair(0, 0); - } - if (IsJSGlobalObject()) { - GlobalDictionary *dict = GlobalDictionary::Cast(array); - return dict->GetNumOfEnumKeys(); - } - - NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject()); - return dict->GetNumOfEnumKeys(); -} - -uint32_t JSObject::GetNumberOfKeys() -{ - DISALLOW_GARBAGE_COLLECTION; - TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); - - if (!array->IsDictionaryMode()) { - return GetJSHClass()->NumberOfProps(); - } - - return NameDictionary::Cast(array)->EntriesCount(); -} - -bool JSObject::GlobalSetProperty(JSThread *thread, const JSHandle &key, - const JSHandle &value, bool mayThrow) -{ - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - ObjectOperator op(thread, key); - if (!op.IsFound()) { - PropertyAttributes attr = PropertyAttributes::Default(true, true, false); - op.SetAttr(attr); - } - return SetProperty(&op, value, mayThrow); -} - -uint32_t JSObject::GetNumberOfElements() -{ - DISALLOW_GARBAGE_COLLECTION; - uint32_t numOfElements = 0; - if (IsJSPrimitiveRef() && JSPrimitiveRef::Cast(this)->IsString()) { - numOfElements = JSPrimitiveRef::Cast(this)->GetStringLength(); - } - - if (!ElementAccessor::IsDictionaryMode(this)) { - uint32_t elementsLen = ElementAccessor::GetElementsLength(this); - for (uint32_t i = 0; i < elementsLen; ++i) { - if (!ElementAccessor::Get(this, i).IsHole()) { - numOfElements++; - } - } - } else { - TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); - numOfElements += static_cast(NumberDictionary::Cast(elements)->EntriesCount()); - } - - return numOfElements; -} - -// 9.1.9 [[Set]] ( P, V, Receiver) -bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const JSHandle &value, const JSHandle &receiver, bool mayThrow) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - // 2 ~ 4 findProperty in Receiver, Obj and its parents - ObjectOperator op(thread, obj, receiver, key); - return SetProperty(&op, value, mayThrow); -} - -bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + { + uint32_t elementIndex = 0; + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + static_cast(offset); + for (uint32_t i = static_cast(offset); i < elementIndex; ++i) { + auto key = base::NumberHelper::IntToEcmaString(thread, i); + keyArray->Set(thread, i, key); + } + } + + if (!ElementAccessor::IsDictionaryMode(obj)) { + uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); + for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { + if (!ElementAccessor::Get(obj, i).IsHole()) { + auto key = base::NumberHelper::IntToEcmaString(thread, i); + keyArray->Set(thread, j++, key); + } + } + } else { + JSHandle elements(thread, obj->GetElements()); + NumberDictionary::GetAllKeys(thread, JSHandle(elements), elementIndex, keyArray); + } + } + + void JSObject::GetAllElementKeysByFilter(JSThread *thread, + const JSHandle &obj, + const JSHandle &keyArray, + uint32_t &keyArrayEffectiveLength, + uint32_t filter) + { + ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); + uint32_t elementIndex = 0; + + // For strings attributes, only enumerable is true + if ((filter & NATIVE_ENUMERABLE) && obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength(); + for (uint32_t i = 0; i < elementIndex; ++i) { + keyArray->Set(thread, keyArrayEffectiveLength, JSTaggedValue(i)); + keyArrayEffectiveLength++; + } + } + + JSHandle objValue(obj); + + if (!ElementAccessor::IsDictionaryMode(obj)) { + uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!ElementAccessor::Get(obj, i).IsHole()) { + ObjectOperator op(thread, objValue, i, OperatorType::OWN); + bool bIgnore = FilterHelper::IgnoreKeyByFilter(op, filter); + if (bIgnore) { + continue; + } + keyArray->Set(thread, keyArrayEffectiveLength, JSTaggedValue(i)); + keyArrayEffectiveLength++; + } + } + } else { + JSHandle elements(thread, obj->GetElements()); + NumberDictionary::GetAllKeysByFilter(thread, JSHandle(elements), + keyArrayEffectiveLength, keyArray, filter); + } + } + + void JSObject::GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector) + { + if (!ElementAccessor::IsDictionaryMode(obj)) { + uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!ElementAccessor::Get(obj, i).IsHole()) { + keyVector.emplace_back(JSTaggedValue(i)); + } + } + } else { + JSHandle elements(thread, obj->GetElements()); + JSHandle dict = JSHandle::Cast(elements); + dict->GetAllKeysIntoVector(keyVector); + } + } + + JSHandle JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfElements, uint32_t *keys) + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle elementArray = factory->NewTaggedArray(numOfElements); + CollectEnumElementsAlongProtoChain(thread, obj, offset, elementArray, keys); + return elementArray; + } + + void JSObject::CollectEnumElementsAlongProtoChain(JSThread *thread, const JSHandle &obj, int offset, + JSHandle elementArray, uint32_t *keys, + int32_t lastLength) + { + uint32_t elementIndex = static_cast(offset); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + uint32_t strLen = JSPrimitiveRef::Cast(*obj)->GetStringLength(); + for (uint32_t i = 0; i < strLen; ++i) { + keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i)); + elementArray->Set(thread, elementIndex, keyHandle); + elementIndex++; + } + *keys += strLen; + } + + if (!ElementAccessor::IsDictionaryMode(obj)) { + JSHandle emptyQueue = thread->GetEcmaVM()->GetFactory()->GetEmptyTaggedQueue(); + uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); + uint32_t preElementIndex = elementIndex; + for (uint32_t i = 0; i < elementsLen; ++i) { + if (ElementAccessor::Get(obj, i).IsHole()) { + continue; + } + keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i)); + bool isDuplicated = IsDepulicateKeys(thread, elementArray, lastLength, emptyQueue, keyHandle); + if (isDuplicated) { + continue; + } + elementArray->Set(thread, elementIndex, keyHandle); + elementIndex++; + } + *keys += (elementIndex - preElementIndex); + } else { + JSHandle arr(thread, obj->GetElements()); + NumberDictionary::GetAllEnumKeys( + thread, JSHandle(arr), elementIndex, elementArray, keys, lastLength); + } + } + + void JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) + { + uint32_t elementIndex = 0; + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + static_cast(offset); + for (uint32_t i = static_cast(offset); i < elementIndex; ++i) { + auto key = base::NumberHelper::IntToEcmaString(thread, i); + keyArray->Set(thread, i, key); + } + } + + if (!ElementAccessor::IsDictionaryMode(obj)) { + uint32_t elementsLen = ElementAccessor::GetElementsLength(obj); + for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { + if (!ElementAccessor::Get(obj, i).IsHole()) { + auto key = base::NumberHelper::IntToEcmaString(thread, i); + keyArray->Set(thread, j++, key); + } + } + } else { + JSHandle elements(thread, obj->GetElements()); + uint32_t keys = 0; + NumberDictionary::GetAllEnumKeys(thread, JSHandle(elements), elementIndex, keyArray, &keys); + } + } + + std::pair JSObject::GetNumberOfEnumKeys() const + { + DISALLOW_GARBAGE_COLLECTION; + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + int end = static_cast(GetJSHClass()->NumberOfProps()); + if (end > 0) { + LayoutInfo *layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject()); + return layout->GetNumOfEnumKeys(end, this); + } + return std::make_pair(0, 0); + } + if (IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(array); + return dict->GetNumOfEnumKeys(); + } + + NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject()); + return dict->GetNumOfEnumKeys(); + } + + uint32_t JSObject::GetNumberOfKeys() + { + DISALLOW_GARBAGE_COLLECTION; + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + + if (!array->IsDictionaryMode()) { + return GetJSHClass()->NumberOfProps(); + } + + return NameDictionary::Cast(array)->EntriesCount(); + } + + bool JSObject::GlobalSetProperty(JSThread *thread, const JSHandle &key, + const JSHandle &value, bool mayThrow) + { + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, key); + if (!op.IsFound()) { + PropertyAttributes attr = PropertyAttributes::Default(true, true, false); + op.SetAttr(attr); + } + return SetProperty(&op, value, mayThrow); + } + + uint32_t JSObject::GetNumberOfElements() + { + DISALLOW_GARBAGE_COLLECTION; + uint32_t numOfElements = 0; + if (IsJSPrimitiveRef() && JSPrimitiveRef::Cast(this)->IsString()) { + numOfElements = JSPrimitiveRef::Cast(this)->GetStringLength(); + } + + if (!ElementAccessor::IsDictionaryMode(this)) { + uint32_t elementsLen = ElementAccessor::GetElementsLength(this); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!ElementAccessor::Get(this, i).IsHole()) { + numOfElements++; + } + } + } else { + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + numOfElements += static_cast(NumberDictionary::Cast(elements)->EntriesCount()); + } + + return numOfElements; + } + + // 9.1.9 [[Set]] ( P, V, Receiver) + bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, bool mayThrow) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 2 ~ 4 findProperty in Receiver, Obj and its parents + ObjectOperator op(thread, obj, receiver, key); + return SetProperty(&op, value, mayThrow); + } + + bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return SetProperty(&op, value, mayThrow); + } + + bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow, SCheckMode sCheckMode) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + if (obj->IsJSSharedArray()) { + return JSSharedArray::SetProperty(thread, obj, key, value, mayThrow, sCheckMode); + } + // 2 ~ 4 findProperty in Receiver, Obj and its parents + ObjectOperator op(thread, obj, key); + return SetProperty(&op, value, mayThrow); + } + + bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value, bool mayThrow) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + + ObjectOperator op(thread, obj, index); + return SetProperty(&op, value, mayThrow); + } + + bool JSObject::SetPropertyForDataDescriptorProxy(JSThread *thread, ObjectOperator *op, + const JSHandle &value, + JSHandle &receiver) + { + ASSERT(receiver->IsJSProxy()); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + if (op->IsElement()) { + key.Update(JSTaggedValue(op->GetElementIndex())); + } else { + key.Update(op->GetKey().GetTaggedValue()); + } + + PropertyDescriptor existDesc(thread); + JSProxy::GetOwnProperty(thread, JSHandle::Cast(receiver), key, existDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!existDesc.IsEmpty()) { + if (existDesc.IsAccessorDescriptor()) { + return false; + } + + if (!existDesc.IsWritable()) { + return false; + } + + PropertyDescriptor valueDesc(thread, value); + return JSProxy::DefineOwnProperty(thread, JSHandle::Cast(receiver), key, valueDesc); + } + return CreateDataProperty(thread, JSHandle(receiver), key, value); + } + + bool JSObject::SetPropertyForDataDescriptor(ObjectOperator *op, const JSHandle &value, + JSHandle &receiver, bool mayThrow, bool isInternalAccessor) + { + JSThread *thread = op->GetThread(); + if (!op->IsWritable()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false); + } + return false; + } + + if (!receiver->IsECMAObject()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Receiver is not a JSObject", false); + } + return false; + } + if (op->IsFound() && receiver->IsJSShared()) { + if (!ClassHelper::MatchFieldType(op->GetSharedFieldType(), value.GetTaggedValue())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), false); + } + return false; + } + } + + if (receiver->IsJSProxy()) { + return SetPropertyForDataDescriptorProxy(thread, op, value, receiver); + } + + // 5e. If existingDescriptor is not undefined, then + bool hasReceiver = false; + if (op->HasReceiver()) { + op->ReLookupPropertyInReceiver(); + isInternalAccessor = false; + if (op->IsAccessorDescriptor()) { + JSTaggedValue ret = ShouldGetValueFromBox(op); + isInternalAccessor = AccessorData::Cast(ret.GetTaggedObject())->IsInternal(); + } + hasReceiver = true; + } + bool isSuccess = true; + if (op->IsFound() && !op->IsOnPrototype()) { + // i. If IsAccessorDescriptor(existingDescriptor) is true, return false. + if (op->IsAccessorDescriptor() && !isInternalAccessor) { + return false; + } + + // ii. If existingDescriptor.[[Writable]] is false, return false. + if (!op->IsWritable()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false); + } + return false; + } + if (hasReceiver && receiver->IsJSShared() && + !ClassHelper::MatchFieldType(op->GetSharedFieldType(), value.GetTaggedValue())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), false); + } + return false; + } + isSuccess = op->UpdateDataValue(JSHandle(receiver), value, isInternalAccessor, mayThrow); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, isSuccess); + } else { + // 5f. Else if Receiver does not currently have a property P, Return CreateDataProperty(Receiver, P, V). + // fixme(hzzhouzebin) this makes SharedArray's frozen no sense. + if (!receiver->IsExtensible(thread) && !(receiver->IsJSSharedArray() && op->IsElement())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible), false); + } + return false; + } + if (hasReceiver || isInternalAccessor) { + return op->AddProperty(JSHandle(receiver), value, PropertyAttributes::Default()); + } else if (op->IsFound() && receiver.GetTaggedValue() != op->GetHolder().GetTaggedValue()) { + return op->AddProperty(JSHandle(receiver), value, PropertyAttributes::Default()); + } else { + return op->AddProperty(JSHandle(receiver), value, op->GetAttr()); + } + } + return isSuccess; + } + + bool JSObject::SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow) + { + JSThread *thread = op->GetThread(); + op->UpdateDetector(); + + JSHandle receiver = op->GetReceiver(); + JSHandle holder = op->GetHolder(); + if (holder->IsJSProxy()) { + if (op->IsElement()) { + JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); + return JSProxy::SetProperty(thread, JSHandle::Cast(holder), key, value, receiver, mayThrow); + } + return JSProxy::SetProperty(thread, JSHandle::Cast(holder), op->GetKey(), value, receiver, mayThrow); + } + + // When op is not found and is not set extra attributes + if (!op->IsFound() && op->IsPrimitiveAttr()) { + op->SetAsDefaultAttr(); + } + + bool isInternalAccessor = false; + if (op->IsAccessorDescriptor()) { + JSTaggedValue ret = ShouldGetValueFromBox(op); + isInternalAccessor = AccessorData::Cast(ret.GetTaggedObject())->IsInternal(); + } + + // 5. If IsDataDescriptor(ownDesc) is true, then + if (!op->IsAccessorDescriptor() || isInternalAccessor) { + return SetPropertyForDataDescriptor(op, value, receiver, mayThrow, isInternalAccessor); + } + // 6. Assert: IsAccessorDescriptor(ownDesc) is true. + ASSERT(op->IsAccessorDescriptor()); + // 8. If setter is undefined, return false. + JSTaggedValue ret = ShouldGetValueFromBox(op); + AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject()); + return CallSetter(thread, *accessor, receiver, value, mayThrow); + } + + bool JSObject::CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle &receiver, const JSHandle &value, bool mayThrow) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - ObjectOperator op(thread, obj, key); - return SetProperty(&op, value, mayThrow); -} - -bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const JSHandle &value, bool mayThrow, SCheckMode sCheckMode) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - if (obj->IsJSSharedArray()) { - return JSSharedArray::SetProperty(thread, obj, key, value, mayThrow, sCheckMode); - } - // 2 ~ 4 findProperty in Receiver, Obj and its parents - ObjectOperator op(thread, obj, key); - return SetProperty(&op, value, mayThrow); -} - -bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, uint32_t index, - const JSHandle &value, bool mayThrow) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - - ObjectOperator op(thread, obj, index); - return SetProperty(&op, value, mayThrow); -} - -bool JSObject::SetPropertyForDataDescriptorProxy(JSThread *thread, ObjectOperator *op, - const JSHandle &value, - JSHandle &receiver) -{ - ASSERT(receiver->IsJSProxy()); - JSMutableHandle key(thread, JSTaggedValue::Undefined()); - if (op->IsElement()) { - key.Update(JSTaggedValue(op->GetElementIndex())); - } else { - key.Update(op->GetKey().GetTaggedValue()); - } - - PropertyDescriptor existDesc(thread); - JSProxy::GetOwnProperty(thread, JSHandle::Cast(receiver), key, existDesc); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - if (!existDesc.IsEmpty()) { - if (existDesc.IsAccessorDescriptor()) { - return false; - } - - if (!existDesc.IsWritable()) { - return false; - } - - PropertyDescriptor valueDesc(thread, value); - return JSProxy::DefineOwnProperty(thread, JSHandle::Cast(receiver), key, valueDesc); - } - return CreateDataProperty(thread, JSHandle(receiver), key, value); -} - -bool JSObject::SetPropertyForDataDescriptor(ObjectOperator *op, const JSHandle &value, - JSHandle &receiver, bool mayThrow, bool isInternalAccessor) -{ - JSThread *thread = op->GetThread(); - if (!op->IsWritable()) { - if (mayThrow) { - THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false); - } - return false; - } - - if (!receiver->IsECMAObject()) { - if (mayThrow) { - THROW_TYPE_ERROR_AND_RETURN(thread, "Receiver is not a JSObject", false); - } - return false; - } - if (op->IsFound() && receiver->IsJSShared()) { - if (!ClassHelper::MatchFieldType(op->GetSharedFieldType(), value.GetTaggedValue())) { - if (mayThrow) { - THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), false); - } - return false; - } - } - - if (receiver->IsJSProxy()) { - return SetPropertyForDataDescriptorProxy(thread, op, value, receiver); - } - - // 5e. If existingDescriptor is not undefined, then - bool hasReceiver = false; - if (op->HasReceiver()) { - op->ReLookupPropertyInReceiver(); - isInternalAccessor = false; - if (op->IsAccessorDescriptor()) { - JSTaggedValue ret = ShouldGetValueFromBox(op); - isInternalAccessor = AccessorData::Cast(ret.GetTaggedObject())->IsInternal(); - } - hasReceiver = true; - } - bool isSuccess = true; - if (op->IsFound() && !op->IsOnPrototype()) { - // i. If IsAccessorDescriptor(existingDescriptor) is true, return false. - if (op->IsAccessorDescriptor() && !isInternalAccessor) { - return false; - } - - // ii. If existingDescriptor.[[Writable]] is false, return false. - if (!op->IsWritable()) { - if (mayThrow) { - THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false); - } - return false; - } - if (hasReceiver && receiver->IsJSShared() && - !ClassHelper::MatchFieldType(op->GetSharedFieldType(), value.GetTaggedValue())) { - if (mayThrow) { - THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), false); - } - return false; - } - isSuccess = op->UpdateDataValue(JSHandle(receiver), value, isInternalAccessor, mayThrow); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, isSuccess); - } else { - // 5f. Else if Receiver does not currently have a property P, Return CreateDataProperty(Receiver, P, V). - // fixme(hzzhouzebin) this makes SharedArray's frozen no sense. - if (!receiver->IsExtensible(thread) && !(receiver->IsJSSharedArray() && op->IsElement())) { - if (mayThrow) { - THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetPropertyWhenNotExtensible), false); - } - return false; - } - if (hasReceiver || isInternalAccessor) { - return op->AddProperty(JSHandle(receiver), value, PropertyAttributes::Default()); - } else if (op->IsFound() && receiver.GetTaggedValue() != op->GetHolder().GetTaggedValue()) { - return op->AddProperty(JSHandle(receiver), value, PropertyAttributes::Default()); - } else { - return op->AddProperty(JSHandle(receiver), value, op->GetAttr()); - } - } - return isSuccess; -} - -bool JSObject::SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow) -{ - JSThread *thread = op->GetThread(); - op->UpdateDetector(); - - JSHandle receiver = op->GetReceiver(); - JSHandle holder = op->GetHolder(); - if (holder->IsJSProxy()) { - if (op->IsElement()) { - JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); - return JSProxy::SetProperty(thread, JSHandle::Cast(holder), key, value, receiver, mayThrow); - } - return JSProxy::SetProperty(thread, JSHandle::Cast(holder), op->GetKey(), value, receiver, mayThrow); - } - - // When op is not found and is not set extra attributes - if (!op->IsFound() && op->IsPrimitiveAttr()) { - op->SetAsDefaultAttr(); - } - - bool isInternalAccessor = false; - if (op->IsAccessorDescriptor()) { - JSTaggedValue ret = ShouldGetValueFromBox(op); - isInternalAccessor = AccessorData::Cast(ret.GetTaggedObject())->IsInternal(); - } - - // 5. If IsDataDescriptor(ownDesc) is true, then - if (!op->IsAccessorDescriptor() || isInternalAccessor) { - return SetPropertyForDataDescriptor(op, value, receiver, mayThrow, isInternalAccessor); - } - // 6. Assert: IsAccessorDescriptor(ownDesc) is true. - ASSERT(op->IsAccessorDescriptor()); - // 8. If setter is undefined, return false. - JSTaggedValue ret = ShouldGetValueFromBox(op); - AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject()); - return CallSetter(thread, *accessor, receiver, value, mayThrow); -} - -bool JSObject::CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle &receiver, - const JSHandle &value, bool mayThrow) -{ - if (UNLIKELY(accessor.IsInternal())) { - return accessor.CallInternalSet(thread, JSHandle::Cast(receiver), value, mayThrow); - } - JSTaggedValue setter = accessor.GetSetter(); - // 8. If setter is undefined, return false. - if (setter.IsUndefined()) { - if (mayThrow) { - THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false); - } - return false; - } - - JSHandle func(thread, setter); - JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 1); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - info->SetCallArg(value.GetTaggedValue()); - JSFunction::Call(info); - - // 10. ReturnIfAbrupt(setterResult). - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - - return true; -} - -JSTaggedValue JSObject::CallGetter(JSThread *thread, const AccessorData *accessor, - const JSHandle &receiver) -{ - JSTaggedValue getter = accessor->GetGetter(); - // 7. If getter is undefined, return undefined. - if (getter.IsUndefined()) { - return JSTaggedValue::Undefined(); - } - - JSHandle func(thread, getter); - JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 0); - JSTaggedValue res = JSFunction::Call(info); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - return res; -} - -// 9.1.8 [[Get]] (P, Receiver) -OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &key, const JSHandle &receiver) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - ObjectOperator op(thread, obj, receiver, key); - return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); -} - -OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &key) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - ObjectOperator op(thread, obj, key); - return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); -} - -OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &key, SCheckMode sCheckMode) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - if (obj->IsJSSharedArray()) { - return JSSharedArray::GetProperty(thread, obj, key, sCheckMode); - } - ObjectOperator op(thread, obj, key); - return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); -} - -OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, uint32_t index) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - - ObjectOperator op(thread, obj, index); - return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); -} - -OperationResult JSObject::GetPropertyFromGlobal(JSThread *thread, const JSHandle &key) -{ - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - ObjectOperator op(thread, key); - return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); -} - -PropertyBox* JSObject::GetGlobalPropertyBox(JSTaggedValue key) -{ - ASSERT(IsJSGlobalObject()); - auto dict = GlobalDictionary::Cast(GetProperties().GetTaggedObject()); - auto entry = dict->FindEntry(key); - if (entry == -1) { - return nullptr; - } - return dict->GetBox(entry); -} - -PropertyBox* JSObject::GetGlobalPropertyBox(JSThread *thread, const std::string& key) -{ - auto factory = thread->GetEcmaVM()->GetFactory(); - auto keyValue = factory->NewFromUtf8(key).GetTaggedValue(); - return GetGlobalPropertyBox(keyValue); -} - -JSTaggedValue JSObject::GetProperty(JSThread *thread, ObjectOperator *op) -{ - JSHandle receiver = op->GetReceiver(); - JSHandle holder = op->GetHolder(); - if (receiver->IsNativeModuleFailureInfo()) { - JSTaggedValue failureInfo = JSHandle::Cast(receiver)->GetArkNativeModuleFailureInfo(); - THROW_REFERENCE_ERROR_AND_RETURN(thread, ConvertToString(failureInfo).c_str(), JSTaggedValue::Undefined()); - } - if (holder->IsJSProxy()) { - if (op->IsElement()) { - JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); - return JSProxy::GetProperty(thread, JSHandle::Cast(holder), key, receiver) - .GetValue() - .GetTaggedValue(); - } - return JSProxy::GetProperty(thread, JSHandle::Cast(holder), op->GetKey(), receiver) - .GetValue() - .GetTaggedValue(); - } - - // 4. If desc is undefined, then - if (!op->IsFound()) { - // 4c. If obj and parent is null, return undefined. - return JSTaggedValue::Undefined(); - } - // 5. If IsDataDescriptor(desc) is true, return desc.[[Value]] - JSTaggedValue ret = ShouldGetValueFromBox(op); - if (!op->IsAccessorDescriptor()) { - return ret; - } - - // 6. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]]. - AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject()); - // 8. Return Call(getter, Receiver). - if (UNLIKELY(accessor->IsInternal())) { - return accessor->CallInternalGet(thread, JSHandle::Cast(holder)); - } - return CallGetter(thread, accessor, receiver); -} - -bool JSObject::DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - SCheckMode sCheckMode) -{ - // 1. Assert: IsPropertyKey(P) is true. - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - // 2. Let desc be O.[[GetOwnProperty]](P). - ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); - - // 4. If desc is undefined, return true. - if (!op.IsFound()) { - return true; - } - // 5. If desc.[[Configurable]] is true, then - // a. Remove the own property with name P from O. - // b. Return true. - // 6. Return false. - if (op.IsConfigurable() || sCheckMode == SCheckMode::SKIP) { - op.DeletePropertyInHolder(); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - obj->GetClass()->SetHasDeleteProperty(true); - return true; - } - return false; -} - -bool JSObject::GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - PropertyDescriptor &desc) -{ - return OrdinaryGetOwnProperty(thread, obj, key, desc); -} - -bool JSObject::GlobalGetOwnProperty(JSThread *thread, const JSHandle &key, PropertyDescriptor &desc) -{ - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - ObjectOperator op(thread, key, OperatorType::OWN); - - if (!op.IsFound()) { - return false; - } - - op.ToPropertyDescriptor(desc); - - if (desc.HasValue()) { - PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject()); - JSHandle valueHandle(thread, cell->GetValue()); - desc.SetValue(valueHandle); - } - ASSERT(!desc.GetValue()->IsInternalAccessor()); - return true; -} - -bool JSObject::OrdinaryGetOwnProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc) -{ - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); - - if (!op.IsFound()) { - return false; - } - - op.ToPropertyDescriptor(desc); - - if (desc.HasValue() && obj->IsJSGlobalObject()) { - JSTaggedValue val = desc.GetValue().GetTaggedValue(); - if (val.IsPropertyBox()) { - PropertyBox *cell = PropertyBox::Cast(val.GetTaggedObject()); - JSHandle valueHandle(thread, cell->GetValue()); - desc.SetValue(valueHandle); - } - } - - return true; -} - -bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const PropertyDescriptor &desc, SCheckMode sCheckMode) -{ - return OrdinaryDefineOwnProperty(thread, obj, key, desc, sCheckMode); -} - -bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, - const PropertyDescriptor &desc, SCheckMode sCheckMode) -{ - return OrdinaryDefineOwnProperty(thread, obj, index, desc, sCheckMode); -} - -// 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc) -bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &key, const PropertyDescriptor &desc, - SCheckMode sCheckMode) -{ - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - // 1. Let current be O.[[GetOwnProperty]](P). - JSHandle objValue(obj); - ObjectOperator op(thread, objValue, key, OperatorType::OWN); - - bool extensible = obj->IsExtensible(); - // make extensible for shared array to add element. - if (obj->IsJSSArray() && op.IsElement()) { - extensible = true; - } - PropertyDescriptor current(thread); - op.ToPropertyDescriptor(current); - // 4. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). - return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current, sCheckMode); -} - -bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, - const PropertyDescriptor &desc, SCheckMode sCheckMode) -{ - JSHandle objValue(obj); - ObjectOperator op(thread, objValue, index, OperatorType::OWN); - - bool extensible = obj->IsExtensible(); - PropertyDescriptor current(thread); - op.ToPropertyDescriptor(current); - return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current, sCheckMode); -} - -bool JSObject::ValidateDataDescriptorWhenConfigurable(ObjectOperator *op, const PropertyDescriptor &desc, - const PropertyDescriptor ¤t, SCheckMode sCheckMode) -{ - // 8a i. Return false, if the [[Writable]] field of current is false and the [[Writable]] field of Desc - // is true. - if (!current.IsWritable() && desc.HasWritable() && desc.IsWritable()) { - return false; - } - // 8a ii. If the [[Writable]] field of current is false, then - if (!current.IsWritable()) { - if (desc.HasValue() && !JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) { - return false; - } - } - if (op->HasHolder() && op->GetHolder()->IsJSShared() && (sCheckMode == SCheckMode::CHECK)) { - if (!desc.HasValue()) { - THROW_TYPE_ERROR_AND_RETURN(op->GetThread(), GET_MESSAGE_STRING(UpdateSendableAttributes), false); - } - if (!ClassHelper::MatchFieldType(current.GetSharedFieldType(), desc.GetValue().GetTaggedValue())) { - THROW_TYPE_ERROR_AND_RETURN(op->GetThread(), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), false); - } - } - return true; -} - -// 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current) -bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc, - const PropertyDescriptor ¤t, SCheckMode sCheckMode) -{ - // 2. If current is undefined, then - if (current.IsEmpty()) { - // 2a. If extensible is false, return false. - if (!(extensible || (op->HasHolder() && op->GetHolder()->IsJSShared() && sCheckMode == SCheckMode::SKIP))) { - return false; - } - if (!op->HasHolder()) { - return true; - } - - // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then - PropertyAttributes attr(desc); - bool success = false; - if (!desc.IsAccessorDescriptor()) { - op->UpdateDetector(); - success = op->AddPropertyInHolder(desc.GetValue(), attr); - } else { // is AccessorDescriptor - // may GC in NewAccessorData, so we need to handle getter and setter. - JSThread *thread = op->GetThread(); - JSHandle accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData(); - if (desc.HasGetter()) { - accessor->SetGetter(thread, desc.GetGetter()); - } - - if (desc.HasSetter()) { - accessor->SetSetter(thread, desc.GetSetter()); - } - op->UpdateDetector(); - success = op->AddPropertyInHolder(JSHandle::Cast(accessor), attr); - } - - return success; - } - - // 3. Return true, if every field in Desc is absent - // 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the - // same value as the corresponding field in current when compared using the SameValue algorithm. - if ((!desc.HasEnumerable() || desc.IsEnumerable() == current.IsEnumerable()) && - (!desc.HasConfigurable() || desc.IsConfigurable() == current.IsConfigurable()) && - (!desc.HasValue() || JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) && - (!desc.HasWritable() || (current.IsWritable() == desc.IsWritable())) && - (!desc.HasGetter() || - (current.HasGetter() && JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter()))) && - (!desc.HasSetter() || - (current.HasSetter() && JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())))) { - return true; - } - - // 5. If the [[Configurable]] field of current is false, then - if (!current.IsConfigurable()) { - // 5a. Return false, if the [[Configurable]] field of Desc is true. - if (desc.HasConfigurable() && desc.IsConfigurable()) { - return false; - } - // b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current - // and Desc are the Boolean negation of each other. - if (desc.HasEnumerable() && (desc.IsEnumerable() != current.IsEnumerable())) { - return false; - } - } - - // 6. If IsGenericDescriptor(Desc) is true, no further validation is required. - if (desc.IsGenericDescriptor()) { - // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then - } else if (current.IsDataDescriptor() != desc.IsDataDescriptor()) { - // 7a. Return false, if the [[Configurable]] field of current is false. - if (!current.IsConfigurable()) { - return false; - } - // 7b. If IsDataDescriptor(current) is true, then - if (current.IsDataDescriptor()) { - // 7bi. If O is not undefined, convert the property named P of object O from a data property to an - // accessor property. Preserve the existing values of the converted property’s [[Configurable]] and - // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. - } else { - // 7ci. If O is not undefined, convert the property named P of object O from an accessor property to a - // data property. Preserve the existing values of the converted property’s [[Configurable]] and - // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. - } - // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then - } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) { - // 8a. If the [[Configurable]] field of current is false, then - if (!current.IsConfigurable() && !ValidateDataDescriptorWhenConfigurable(op, desc, current, sCheckMode)) { - return false; - } - // 8b. Else the [[Configurable]] field of current is true, so any change is acceptable. - } else { // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true, - // 9a. If the [[Configurable]] field of current is false, then - if (!current.IsConfigurable()) { - // i. Return false, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) - // is false. - if (desc.HasSetter() && !JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())) { - return false; - } - // ii. Return false, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], - // current.[[Get]]) is false. - if (desc.HasGetter() && !JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter())) { - return false; - } - } - } - - if (op->HasHolder()) { - // 10. If O is not undefined, then - // a. For each field of Desc that is present, set the corresponding attribute of the property named P of object - // O to the value of the field. - if (!desc.HasValue() && desc.HasWritable() && current.HasValue()) { - // [[Value]] and [[Writable]] attributes are set to the value of the corresponding field in Desc - // if Desc has that field or to the attribute's default value otherwise. - PropertyDescriptor newDesc = desc; - JSHandle valueHandle = current.GetValue(); - if (valueHandle->IsPropertyBox()) { - JSTaggedValue value = PropertyBox::Cast(valueHandle->GetTaggedObject())->GetValue(); - valueHandle = JSHandle(op->GetThread(), value); - } - newDesc.SetValue(valueHandle); - op->UpdateDetector(); - return op->WriteDataPropertyInHolder(newDesc); - } - op->UpdateDetector(); - return op->WriteDataPropertyInHolder(desc); - } - return true; -} - -// 9.1.6.2 IsCompatiblePropertyDescriptor (Extensible, Desc, Current) -bool JSObject::IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc, - const PropertyDescriptor ¤t) -{ - // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current). - ObjectOperator op; - return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); -} - -JSTaggedValue JSObject::GetPrototype(const JSHandle &obj) -{ - JSHClass *hclass = obj->GetJSHClass(); - return hclass->GetPrototype(); -} - -bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, - const JSHandle &proto, - bool isChangeProto) -{ - ASSERT_PRINT(proto->IsECMAObject() || proto->IsNull(), "proto must be object or null"); - JSTaggedValue current = JSObject::GetPrototype(obj); - if (current == proto.GetTaggedValue()) { - return true; - } - if (!obj->IsExtensible()) { - return false; - } - bool done = false; - JSMutableHandle tempProtoHandle(thread, proto.GetTaggedValue()); - while (!done) { - if (tempProtoHandle->IsNull() || !tempProtoHandle->IsECMAObject()) { - done = true; - } else if (JSTaggedValue::SameValue(tempProtoHandle.GetTaggedValue(), obj.GetTaggedValue())) { - return false; - } else { - if (tempProtoHandle->IsJSProxy()) { - break; - } - tempProtoHandle.Update(JSTaggedValue::GetPrototype(thread, JSHandle(tempProtoHandle))); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - } - } - ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); - // map transition - JSHClass::SetPrototypeTransition(thread, obj, proto, isChangeProto); - TryMigrateToGenericKindForJSObject(thread, obj, oldKind); - return true; -} - -bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) -{ - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - JSHandle objValue(obj); - ObjectOperator op(thread, objValue, key); - - JSHandle holder = op.GetHolder(); - if (holder->IsJSProxy()) { - return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); - } - - return op.IsFound(); -} - -bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, uint32_t index) -{ - JSHandle objValue(obj); - ObjectOperator op(thread, objValue, index); - - JSHandle holder = op.GetHolder(); - if (holder->IsJSProxy()) { - JSHandle key(thread, JSTaggedValue(index)); - return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); - } - - return op.IsFound(); -} - -bool JSObject::PreventExtensions(JSThread *thread, const JSHandle &obj) -{ - if (obj->IsExtensible()) { - JSHandle jshclass(thread, obj->GetJSHClass()); - JSHandle newHclass = JSHClass::TransitionExtension(thread, jshclass); -#if ECMASCRIPT_ENABLE_IC - JSHClass::NotifyHclassChanged(thread, jshclass, newHclass); -#endif - ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); - JSHClass::RestoreElementsKindToGeneric(*newHclass); - obj->SynchronizedSetClass(thread, *newHclass); - TryMigrateToGenericKindForJSObject(thread, obj, oldKind); - } - - return true; -} - -// 9.1.12 [[OwnPropertyKeys]] ( ) -JSHandle JSObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj) -{ - uint32_t numOfElements = obj->GetNumberOfElements(); - uint32_t keyLen = numOfElements + obj->GetNumberOfKeys(); - - JSHandle keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen); - - if (numOfElements > 0) { - GetAllElementKeys(thread, obj, 0, keyArray); - } - GetAllKeys(thread, obj, static_cast(numOfElements), keyArray); - return keyArray; -} - -JSHandle JSObject::GetAllPropertyKeys(JSThread *thread, const JSHandle &obj, uint32_t filter) -{ - JSMutableHandle currentObj(thread, obj); - JSMutableHandle currentObjValue(thread, currentObj); - - uint32_t curObjNumberOfElements = currentObj->GetNumberOfElements(); - uint32_t curObjNumberOfKeys = currentObj->GetNumberOfKeys(); - uint32_t curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys; - uint32_t retArrayLength = curObjectKeysLength; - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSMutableHandle retArray(thread, factory->NewTaggedArray(retArrayLength)); - uint32_t retArrayEffectivelength = 0; - - do { - curObjNumberOfElements = currentObj->GetNumberOfElements(); - curObjNumberOfKeys = currentObj->GetNumberOfKeys(); - curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys; - uint32_t minRequireLength = curObjectKeysLength + retArrayEffectivelength; - if (retArrayLength < minRequireLength) { - // expand retArray - if (retArrayLength != 0) { - retArray.Update(factory->NewAndCopyTaggedArray(retArray, minRequireLength, retArrayLength)); - } else { - retArray.Update(factory->NewTaggedArray(minRequireLength)); - } - retArrayLength = minRequireLength; - } - - GetAllElementKeysByFilter(thread, currentObj, retArray, retArrayEffectivelength, filter); - - GetAllKeysByFilter(thread, currentObj, retArrayEffectivelength, retArray, filter); - bool isInculdePrototypes = (filter & NATIVE_KEY_INCLUDE_PROTOTYPES); - if (!isInculdePrototypes) { - break; - } - currentObj.Update(GetPrototype(currentObj)); - currentObjValue.Update(currentObj); - } while (currentObjValue->IsHeapObject()); - - if (retArrayEffectivelength == 0 && (filter & NATIVE_KEY_OWN_ONLY)) { - return retArray; - } - JSMutableHandle element(thread, JSTaggedValue::Undefined()); - if (filter & NATIVE_KEY_NUMBERS_TO_STRINGS) { - for (uint32_t i = 0; i < retArrayEffectivelength; i++) { - element.Update(retArray->Get(i)); - if (element->IsNumber()) { - retArray->Set(thread, i, base::NumberHelper::NumberToString(thread, - JSTaggedValue(element->GetNumber()))); - } - } - } - uint32_t elementIndex = 0; - if (filter & NATIVE_KEY_SKIP_STRINGS) { - while ((retArrayEffectivelength > 0) && (elementIndex < retArrayEffectivelength)) { - if (retArray->Get(elementIndex).IsString()) { - TaggedArray::RemoveElementByIndex(thread, retArray, elementIndex, retArrayEffectivelength); - retArrayEffectivelength--; - } else { - elementIndex++; - } - } - } - if (retArray->GetLength() > retArrayEffectivelength) { - retArray->Trim(thread, retArrayEffectivelength); - } - return retArray; -} - -void JSObject::CollectEnumKeysAlongProtoChain(JSThread *thread, const JSHandle &obj, - JSHandle keyArray, uint32_t *keys, - JSHandle shadowQueue, int32_t lastLength) -{ - ASSERT(!obj->IsJSGlobalObject()); - - TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); - if (!array->IsDictionaryMode()) { - JSHClass *jsHclass = obj->GetJSHClass(); - int end = static_cast(jsHclass->NumberOfProps()); - if (end > 0) { - LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()) - ->GetAllEnumKeys(thread, end, *keys, keyArray, keys, shadowQueue, obj, lastLength); - } - return; - } - NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->GetAllEnumKeys(thread, *keys, keyArray, keys, shadowQueue, lastLength); -} - -void JSObject::AppendOwnEnumPropertyKeys(JSThread *thread, const JSHandle &obj, - JSHandle keyArray, uint32_t *keys, - JSHandle shadowQueue) -{ - int32_t lastLength = *keys; - uint32_t numOfElements = obj->GetNumberOfElements(); - if (numOfElements > 0) { - CollectEnumElementsAlongProtoChain(thread, obj, *keys, keyArray, keys, lastLength); - } - CollectEnumKeysAlongProtoChain(thread, obj, keyArray, keys, shadowQueue, lastLength); -} - -JSHandle JSObject::GetOwnEnumPropertyKeys(JSThread *thread, const JSHandle &obj) -{ - uint32_t numOfElements = obj->GetNumberOfElements(); - uint32_t keyLen = numOfElements + obj->GetNumberOfKeys(); - - JSHandle keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen); - - if (numOfElements > 0) { - GetEnumElementKeys(thread, obj, 0, keyArray); - } - GetAllEnumKeys(thread, obj, static_cast(numOfElements), keyArray); - return keyArray; -} - -JSHandle JSObject::ObjectCreate(JSThread *thread, const JSHandle &proto) -{ - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - JSHandle constructor(env->GetObjectFunction()); - JSHandle objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(constructor); - SetPrototype(thread, objHandle, JSHandle(proto)); - return objHandle; -} - -// 7.3.4 CreateDataProperty (O, P, V) -bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const JSHandle &value, SCheckMode sCheckMode) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - if (!JSHandle::Cast(obj)->IsJSShared()) { - sCheckMode = SCheckMode::CHECK; - } - auto result = ObjectFastOperator::SetPropertyByValue( - thread, obj.GetTaggedValue(), key.GetTaggedValue(), value.GetTaggedValue(), sCheckMode); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - if (!result.IsHole()) { - return !result.IsException(); - } - PropertyDescriptor desc(thread, value, true, true, true); - return JSTaggedValue::DefineOwnProperty(thread, JSHandle::Cast(obj), key, desc, sCheckMode); -} - -bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, uint32_t index, - const JSHandle &value, SCheckMode sCheckMode) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - auto result = ObjectFastOperator::SetPropertyByIndex - (thread, obj.GetTaggedValue(), index, value.GetTaggedValue()); - if (!result.IsHole()) { - return !result.IsException(); - } - PropertyDescriptor desc(thread, value, true, true, true); - return DefineOwnProperty(thread, obj, index, desc, sCheckMode); -} - -// 7.3.5 CreateMethodProperty (O, P, V) -bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, - const JSHandle &key, const JSHandle &value, - SCheckMode sCheckMode) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - bool success = CreateDataProperty(thread, obj, key, value, sCheckMode); - if (!success) { - THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); - } - return success; -} - -bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, uint32_t index, - const JSHandle &value, SCheckMode sCheckMode) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - - bool success = CreateDataProperty(thread, obj, index, value, sCheckMode); - if (!success) { - THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); - } - return success; -} -// 7.3.6 CreateDataPropertyOrThrow (O, P, V) -bool JSObject::CreateMethodProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const JSHandle &value) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - - PropertyDescriptor desc(thread, value, true, false, true); - return DefineOwnProperty(thread, obj, key, desc); -} - -JSHandle JSObject::CallFunction(JSThread *thread, const JSHandle &func) -{ - if (func->IsUndefined() || func->IsNull()) { - return JSHandle(thread, JSTaggedValue::Undefined()); - } - if (!func->IsCallable()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", func); - } - return func; -} - -// 7.3.9 GetMethod (O, P) -JSHandle JSObject::GetMethod(JSThread *thread, const JSHandle &obj, - const JSHandle &key) -{ - JSHandle func = JSTaggedValue::GetProperty(thread, obj, key).GetValue(); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - func = CallFunction(thread, func); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - return func; -} - -JSHandle JSObject::FastGetMethod(JSThread *thread, const JSHandle &obj, - const JSHandle &key) -{ - JSHandle func(thread, ObjectFastOperator::FastGetPropertyByName(thread, obj.GetTaggedValue(), - key.GetTaggedValue())); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - func = CallFunction(thread, func); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - return func; -} - -// 7.3.14 SetIntegrityLevel (O, level) -bool JSObject::SetIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), - "level is not a valid IntegrityLevel"); - - bool status = JSTaggedValue::PreventExtensions(thread, JSHandle(obj)); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - if (!status) { - return false; - } - - JSHandle jshandleKeys = - JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - PropertyDescriptor descNoConf(thread); - descNoConf.SetConfigurable(false); - PropertyDescriptor descNoConfWrite(thread); - descNoConfWrite.SetWritable(false); - descNoConfWrite.SetConfigurable(false); - - if (level == IntegrityLevel::SEALED) { - uint32_t length = jshandleKeys->GetLength(); - if (length == 0) { - return true; - } - auto key = jshandleKeys->Get(0); - JSMutableHandle handleKey(thread, key); - for (uint32_t i = 0; i < length; i++) { - auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); - handleKey.Update(taggedKey); - [[maybe_unused]] bool success = - JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, descNoConf); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - } - } else { - uint32_t length = jshandleKeys->GetLength(); - if (length == 0) { - return true; - } - auto key = jshandleKeys->Get(0); - JSMutableHandle handleKey(thread, key); - for (uint32_t i = 0; i < length; i++) { - auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); - handleKey.Update(taggedKey); - PropertyDescriptor currentDesc(thread); - bool curDescStatus = - JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), handleKey, currentDesc); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - if (curDescStatus) { - PropertyDescriptor desc = currentDesc.IsAccessorDescriptor() ? descNoConf : descNoConfWrite; - [[maybe_unused]] bool success = - JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, desc); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - } - } - } - return true; -} - -bool JSObject::FreezeSharedObject(JSThread *thread, const JSHandle &obj) -{ - ASSERT_PRINT(JSHandle(obj)->IsJSSharedObject() || - JSHandle(obj)->IsJSSharedFunction() || - JSHandle(obj)->IsJSSharedAsyncFunction(), - "Obj is not a valid shared object"); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - // It is not extensible for shared object. - if (obj->IsExtensible()) { - return false; - } - JSHandle hclass(thread, obj->GetClass()); - auto newClass = JSHClass::Clone(thread, hclass); - if (!hclass->IsDictionaryMode()) { - uint32_t propNumber = hclass->NumberOfProps(); - JSHandle layoutInfo(thread, hclass->GetLayout()); - JSHandle newLayoutInfo = factory->CreateSLayoutInfo(propNumber); - for (uint32_t i = 0; i < propNumber; i++) { - JSTaggedValue key = layoutInfo->GetKey(i); - PropertyAttributes attr = layoutInfo->GetAttr(i); - attr.SetWritable(false); - newLayoutInfo->AddKey(thread, i, key, attr); - } - newClass->SetLayout(thread, newLayoutInfo); - obj->SynchronizedSetClass(thread, *newClass); - } else { - auto dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); - dict->UpdateAllAttributesToNoWitable(thread); - } - return true; -} - -// 7.3.15 TestIntegrityLevel (O, level) -bool JSObject::TestIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), - "level is not a valid IntegrityLevel"); - - bool status = JSHandle(obj)->IsExtensible(thread); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - if (status) { - return false; - } - - JSHandle jshandleKeys = - JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - uint32_t length = jshandleKeys->GetLength(); - if (length == 0) { - return true; - } - auto key = jshandleKeys->Get(0); - JSMutableHandle handleKey(thread, key); - for (uint32_t i = 0; i < length; i++) { - auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); - handleKey.Update(taggedKey); - PropertyDescriptor currentDesc(thread); - bool curDescStatus = - JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), handleKey, currentDesc); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - if (curDescStatus) { - if (currentDesc.IsConfigurable()) { - return false; - } - if (level == IntegrityLevel::FROZEN && - currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) { - return false; - } - } - } - return true; -} - -// 7.3.21 EnumerableOwnNames (O) -JSHandle JSObject::EnumerableOwnNames(JSThread *thread, const JSHandle &obj) -{ - ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle tagObj(obj); - // fast mode - if (tagObj->IsJSObject() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) { - uint32_t copyLengthOfKeys = 0; - uint32_t copyLengthOfElements = 0; - auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, ©LengthOfKeys, ©LengthOfElements); - JSHandle keyArray = keyElementPair.first; - JSHandle elementArray = keyElementPair.second; - JSHandle keys; - if (copyLengthOfKeys != 0 && copyLengthOfElements != 0) { - keys = TaggedArray::AppendSkipHole(thread, elementArray, keyArray, copyLengthOfKeys + copyLengthOfElements); - } else if (copyLengthOfKeys != 0) { - if (copyLengthOfKeys < keyArray->GetLength()) { - // keyArray will skip nonEnumerable properties, need re-set length. - keyArray->Trim(thread, copyLengthOfKeys); - } - return keyArray; - } else if (copyLengthOfElements != 0) { - if (copyLengthOfElements < elementArray->GetLength()) { - // elementArray will skip hole value, need re-set length. - elementArray->Trim(thread, copyLengthOfElements); - } - return elementArray; - } else { - keys = factory->EmptyArray(); - } - return keys; - } - - uint32_t copyLength = 0; - JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); - uint32_t length = keys->GetLength(); - - JSHandle names = factory->NewTaggedArray(length); - JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); - for (uint32_t i = 0; i < length; i++) { - keyHandle.Update(keys->Get(i)); - if (keyHandle->IsString()) { - PropertyDescriptor desc(thread); - bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), - keyHandle, desc); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); - - if (status && desc.IsEnumerable()) { - names->Set(thread, copyLength, keyHandle); - copyLength++; - } - } - } - - return factory->CopyArray(names, length, copyLength); -} - -void JSObject::EnumerableOwnPropertyNamesHelper(JSThread *thread, const JSHandle &obj, - const JSHandle &arr, JSHandle &prop, uint32_t &index, bool &fastMode, PropertyKind kind) -{ - JSMutableHandle key(thread, JSTaggedValue::Undefined()); - JSHandle objClass(thread, obj->GetJSHClass()); - uint32_t length = arr->GetLength(); - for (uint32_t i = 0; i < length; i++) { - key.Update(arr->Get(thread, i)); - if (!JSTaggedValue::IsPropertyKey(key)) { - break; - } - JSTaggedValue value = JSTaggedValue::Hole(); - if (fastMode) { - value = ObjectFastOperator::GetPropertyByValue - (thread, obj.GetTaggedValue(), key.GetTaggedValue()); - RETURN_IF_ABRUPT_COMPLETION(thread); - } - if (value.IsHole()) { - PropertyDescriptor desc(thread); - bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), key, desc); - RETURN_IF_ABRUPT_COMPLETION(thread); - if (!status || !desc.IsEnumerable()) { - continue; - } - if (desc.HasValue()) { - value = desc.GetValue().GetTaggedValue(); - } else { - OperationResult opResult = JSTaggedValue::GetProperty(thread, JSHandle::Cast(obj), key); - RETURN_IF_ABRUPT_COMPLETION(thread); - value = opResult.GetValue().GetTaggedValue(); - } - } - index = SetValuesOrEntries(thread, prop, index, key, JSHandle(thread, value), kind); - fastMode = fastMode ? CheckHClassHit(obj, objClass) : fastMode; - } -} - -JSHandle JSObject::EnumerableOwnPropertyNames(JSThread *thread, const JSHandle &obj, - PropertyKind kind) -{ - // 1. Assert: Type(O) is Object. - ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); - - // 2. Let ownKeys be ? O.[[OwnPropertyKeys]](). - JSHandle tagObj(obj); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - if (tagObj->IsJSObject() && !tagObj->IsJSProxy() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) { - uint32_t copyLengthOfKeys = 0; - uint32_t copyLengthOfElements = 0; - uint32_t index = 0; - bool fastMode = true; - auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, ©LengthOfKeys, ©LengthOfElements); - JSHandle keyArray = keyElementPair.first; - JSHandle elementArray = keyElementPair.second; - JSHandle properties = factory->NewTaggedArray(copyLengthOfKeys + copyLengthOfElements); - if (copyLengthOfElements != 0) { - EnumerableOwnPropertyNamesHelper(thread, obj, elementArray, properties, index, fastMode, kind); - } - if (copyLengthOfKeys != 0) { - EnumerableOwnPropertyNamesHelper(thread, obj, keyArray, properties, index, fastMode, kind); - } - if (UNLIKELY(!fastMode && index < copyLengthOfKeys + copyLengthOfElements)) { - properties->Trim(thread, index); - } - return properties; - } - - JSHandle ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); - - // 3. Let properties be a new empty List. - uint32_t length = ownKeys->GetLength(); - JSHandle properties = factory->NewTaggedArray(length); - - // 4. For each element key of ownKeys, do - // a. If Type(key) is String, then - // i. Let desc be ? O.[[GetOwnProperty]](key). - // ii. If desc is not undefined and desc.[[Enumerable]] is true, then - // 1. If kind is key, append key to properties. - // 2. Else, - // a. Let value be ? Get(O, key). - // b. If kind is value, append value to properties. - // c. Else, - // i. Assert: kind is key+value. - // ii. Let entry be ! CreateArrayFromList(« key, value »). - // iii. Append entry to properties. - JSMutableHandle key(thread, JSTaggedValue::Undefined()); - uint32_t index = 0; - for (uint32_t i = 0; i < length; i++) { - key.Update(ownKeys->Get(thread, i)); - if (key->IsString()) { - PropertyDescriptor desc(thread); - bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), - key, desc); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); - if (status && desc.IsEnumerable()) { - if (kind == PropertyKind::KEY) { - properties->Set(thread, index++, key); - } else { - OperationResult result = - JSTaggedValue::GetProperty(thread, JSHandle::Cast(obj), key); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); - JSHandle value = result.GetValue(); - index = SetValuesOrEntries(thread, properties, index, key, value, kind); - } - } - } - } - - if (UNLIKELY(index < length)) { - properties->Trim(thread, index); - } - // 5. Return properties. - return properties; -} - -JSHandle JSObject::GetFunctionRealm(JSThread *thread, const JSHandle &object) -{ - // 1. Assert: obj is a callable object. - ASSERT(object->IsCallable()); - // 2. If obj has a [[Realm]] internal slot, then return obj’s [[Realm]] internal slot. - // 3. If obj is a Bound Function exotic object, then - if (object->IsBoundFunction()) { - // a. Let target be obj’s [[BoundTargetFunction]] internal slot. - JSHandle target(thread, JSHandle(object)->GetBoundTarget()); - // b. Return GetFunctionRealm(target). - return GetFunctionRealm(thread, target); - } - // 4. If obj is a Proxy exotic object, then - if (object->IsJSProxy()) { - // a. If the value of the [[ProxyHandler]] internal slot of obj is null, throw a TypeError exception. - if (JSHandle(object)->GetHandler().IsNull()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "JSObject::GetFunctionRealm: handler is null", - JSHandle(thread, JSTaggedValue::Exception())); - } - // b. Let proxyTarget be the value of obj’s [[ProxyTarget]] internal slot. - JSHandle proxyTarget(thread, JSHandle(object)->GetTarget()); - return GetFunctionRealm(thread, proxyTarget); - } - - if (object->IsJSShared()) { - // LexicalEnv in sharedConstructor is constructor itself. And Shared Constructors shares the same GlobalEnv. - return thread->GetEcmaVM()->GetGlobalEnv(); - } - - JSTaggedValue maybeGlobalEnv = JSHandle(object)->GetLexicalEnv(); - while (!maybeGlobalEnv.IsJSGlobalEnv()) { - if (maybeGlobalEnv.IsUndefined()) { - return thread->GetEcmaVM()->GetGlobalEnv(); - } - maybeGlobalEnv = LexicalEnv::Cast(maybeGlobalEnv.GetTaggedObject())->GetParentEnv(); - } - return JSHandle(thread, maybeGlobalEnv); -} - -bool JSObject::InstanceOf(JSThread *thread, const JSHandle &object, - const JSHandle &target) -{ - // 1. If Type(target) is not Object, throw a TypeError exception. - if (!target->IsECMAObject()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when type of target is not Object", false); - } - - EcmaVM *vm = thread->GetEcmaVM(); - // 2. Let instOfHandler be GetMethod(target, @@hasInstance). - JSHandle instOfHandler = FastGetMethod(thread, target, vm->GetGlobalEnv()->GetHasInstanceSymbol()); - - // 3. ReturnIfAbrupt(instOfHandler). - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - - // 4. If instOfHandler is not undefined, then - if (!instOfHandler->IsUndefined()) { - // a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)). - JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - EcmaRuntimeCallInfo *info = - EcmaInterpreter::NewRuntimeCallInfo(thread, instOfHandler, target, undefined, 1); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - info->SetCallArg(object.GetTaggedValue()); - JSTaggedValue tagged = JSFunction::Call(info); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); - return tagged.ToBoolean(); - } - - // 5. If IsCallable(target) is false, throw a TypeError exception. - if (!target->IsCallable()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", false); - } - - // 6. Return ? OrdinaryHasInstance(target, object). - return JSFunction::OrdinaryHasInstance(thread, target, object); -} - -// ecma6.0 6.2.4.4 -JSHandle JSObject::FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc) -{ - // 1. If Desc is undefined, return undefined - if (desc.IsEmpty()) { - return JSHandle(thread, JSTaggedValue::Undefined()); - } - - // 2. Let obj be ObjectCreate(%ObjectPrototype%). - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - JSHandle objFunc(env->GetObjectFunction()); - JSHandle objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(objFunc); - - auto globalConst = thread->GlobalConstants(); - // 4. If Desc has a [[Value]] field, then Perform CreateDataProperty(obj, "value", Desc.[[Value]]). - if (desc.HasValue()) { - JSHandle valueStr = globalConst->GetHandledValueString(); - bool success = CreateDataProperty(thread, objHandle, valueStr, desc.GetValue()); - RASSERT_PRINT(success, "CreateDataProperty must be success"); - } - // 5. If Desc has a [[Writable]] field, then Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]). - if (desc.HasWritable()) { - JSHandle writableStr = globalConst->GetHandledWritableString(); - JSHandle writable(thread, JSTaggedValue(desc.IsWritable())); - [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, writableStr, writable); - ASSERT_PRINT(success, "CreateDataProperty must be success"); - } - // 6. If Desc has a [[Get]] field, then Perform CreateDataProperty(obj, "get", Desc.[[Get]]). - if (desc.HasGetter()) { - JSHandle getStr = globalConst->GetHandledGetString(); - bool success = CreateDataProperty(thread, objHandle, getStr, desc.GetGetter()); - RASSERT_PRINT(success, "CreateDataProperty must be success"); - } - // 7. If Desc has a [[Set]] field, then Perform CreateDataProperty(obj, "set", Desc.[[Set]]) - if (desc.HasSetter()) { - JSHandle setStr = globalConst->GetHandledSetString(); - bool success = CreateDataProperty(thread, objHandle, setStr, desc.GetSetter()); - RASSERT_PRINT(success, "CreateDataProperty must be success"); - } - // 8. If Desc has an [[Enumerable]] field, then Perform CreateDataProperty(obj, "enumerable", - // Desc.[[Enumerable]]). - if (desc.HasEnumerable()) { - JSHandle enumerableStr = globalConst->GetHandledEnumerableString(); - JSHandle enumerable(thread, JSTaggedValue(desc.IsEnumerable())); - [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, enumerableStr, enumerable); - ASSERT_PRINT(success, "CreateDataProperty must be success"); - } - // 9. If Desc has a [[Configurable]] field, then Perform CreateDataProperty(obj , "configurable", - // Desc.[[Configurable]]). - if (desc.HasConfigurable()) { - JSHandle configurableStr = globalConst->GetHandledConfigurableString(); - JSHandle configurable(thread, JSTaggedValue(desc.IsConfigurable())); - [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, configurableStr, configurable); - ASSERT_PRINT(success, "CreateDataProperty must be success"); - } - return JSHandle(objHandle); -} - -bool JSObject::ToPropertyDescriptorFast(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) -{ - auto *hclass = obj->GetTaggedObject()->GetClass(); - JSType jsType = hclass->GetObjectType(); - if (jsType != JSType::JS_OBJECT) { - return false; - } - if (hclass->IsDictionaryMode()) { - return false; - } - auto env = thread->GetEcmaVM()->GetGlobalEnv(); - auto globalConst = thread->GlobalConstants(); - if (hclass->GetPrototype() != env->GetObjectFunctionPrototype().GetTaggedValue()) { - return false; - } - if (JSObject::Cast(hclass->GetPrototype().GetTaggedObject())->GetClass() != - env->GetObjectFunctionPrototypeClass().GetObject()) { - return false; - } - LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); - uint32_t propsNumber = hclass->NumberOfProps(); - for (uint32_t i = 0; i < propsNumber; i++) { - auto attr = layoutInfo->GetAttr(i); - if (attr.IsAccessor()) { - return false; - } - auto key = layoutInfo->GetKey(i); - auto value = JSObject::Cast(obj->GetTaggedObject())->GetProperty(hclass, attr); - if (key == globalConst->GetEnumerableString()) { - bool enumerable = value.ToBoolean(); - desc.SetEnumerable(enumerable); - } else if (key == globalConst->GetConfigurableString()) { - bool configurable = value.ToBoolean(); - desc.SetConfigurable(configurable); - } else if (key == globalConst->GetValueString()) { - auto handleValue = JSHandle(thread, value); - desc.SetValue(handleValue); - } else if (key == globalConst->GetWritableString()) { - bool writable = value.ToBoolean(); - desc.SetWritable(writable); - } else if (key == globalConst->GetGetString()) { - if (!value.IsCallable()) { - return false; - } - auto getter = JSHandle(thread, value); - desc.SetGetter(getter); - } else if (key == globalConst->GetSetString()) { - if (!value.IsCallable()) { - return false; - } - auto setter = JSHandle(thread, value); - desc.SetSetter(setter); - } - } - - if (desc.IsAccessorDescriptor()) { - // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. - if (desc.HasValue() || desc.HasWritable()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "either Value or Writable is present", true); - } - } - return true; -} - -// ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj ) -void JSObject::ToPropertyDescriptor(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) -{ - if (!obj->IsECMAObject()) { - // 2. If Type(Obj) is not Object, throw a TypeError exception. - THROW_TYPE_ERROR(thread, "ToPropertyDescriptor error obj is not Object"); - } - - if (ToPropertyDescriptorFast(thread, obj, desc)) { - return; - } - auto globalConst = thread->GlobalConstants(); - // 3. Let desc be a new Property Descriptor that initially has no fields. - // 4. Let hasEnumerable be HasProperty(Obj, "enumerable") - { - ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetEnumerableString()); - if (op.IsFound()) { - auto value = op.FastGetValue(); - bool enumerable = value->IsException() ? false : value->ToBoolean(); - desc.SetEnumerable(enumerable); - } - } - // 7. Let hasConfigurable be HasProperty(Obj, "configurable"). - { - ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetConfigurableString()); - if (op.IsFound()) { - auto value = op.FastGetValue(); - bool conf = value->IsException() ? false : value->ToBoolean(); - desc.SetConfigurable(conf); - } - } - // 10. Let hasValue be HasProperty(Obj, "value"). - { - ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetValueString()); - if (op.IsFound()) { - JSHandle prop = op.FastGetValue(); - desc.SetValue(prop); - } - } - // 13. Let hasWritable be HasProperty(Obj, "writable"). - { - ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetWritableString()); - if (op.IsFound()) { - auto value = op.FastGetValue(); - bool writable = value->IsException() ? false : value->ToBoolean(); - desc.SetWritable(writable); - } - } - // 16. Let hasGet be HasProperty(Obj, "get"). - { - ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetGetString()); - if (op.IsFound()) { - JSHandle getter = op.FastGetValue(); - if (!getter->IsCallable() && !getter->IsUndefined()) { - THROW_TYPE_ERROR(thread, "getter not callable or undefined"); - } - desc.SetGetter(getter); - } - } - - // 19. Let hasSet be HasProperty(Obj, "set"). - { - ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetSetString()); - if (op.IsFound()) { - JSHandle setter = op.FastGetValue(); - if (!setter->IsCallable() && !setter->IsUndefined()) { - THROW_TYPE_ERROR(thread, "setter not callable or undefined"); - } - desc.SetSetter(setter); - } - } - - // 22. If either desc.[[Get]] or desc.[[Set]] is present, then - if (desc.IsAccessorDescriptor()) { - // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. - if (desc.HasValue() || desc.HasWritable()) { - THROW_TYPE_ERROR(thread, "either desc.[[Value]] or desc.[[Writable]] is present"); - } - } - // 23. Return desc. -} - -const CString JSObject::ExtractConstructorAndRecordName(JSThread *thread, TaggedObject *obj, bool noAllocate, - bool *isCallGetter) -{ - CString result = ""; - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); - - JSHandle contructorKey = globalConst->GetHandledConstructorString(); - JSTaggedValue objConstructor = ObjectFastOperator::GetPropertyByName(thread, JSTaggedValue(obj), - contructorKey.GetTaggedValue(), noAllocate, - isCallGetter); - if (*isCallGetter) { - return "JSObject"; - } - - if (!objConstructor.IsJSFunction()) { - return "JSObject"; - } - - JSFunctionBase *func = JSFunctionBase::Cast(objConstructor.GetTaggedObject()); - Method *method = Method::Cast(func->GetMethod().GetTaggedObject()); - MethodLiteral *methodLiteral = method->GetMethodLiteral(); - if (methodLiteral == nullptr) { - return "JSObject"; - } - const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); - panda_file::File::EntityId methodId = methodLiteral->GetMethodId(); - const CString &nameStr = MethodLiteral::ParseFunctionNameToCString(jsPandaFile, methodId); - const CString &moduleStr = method->GetRecordNameStr(); - - if (!moduleStr.empty()) { - result.append(moduleStr).append(" "); - } - if (nameStr.empty()) { - result.append("JSObject"); - } else { - result.append(nameStr); - } - DebugInfoExtractor *debugExtractor = - JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); - if (debugExtractor == nullptr) { - return result; - } - int32_t line = debugExtractor->GetFristLine(methodId); - std::string fileName = debugExtractor->GetSourceFile(methodId); - return result.append("(").append(fileName).append(std::to_string(line)).append(")"); -} - -JSHandle JSObject::SpeciesConstructor(JSThread *thread, const JSHandle &obj, - const JSHandle &defaultConstructor) -{ - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); - // Assert: Type(O) is Object. - ASSERT_PRINT(obj->IsECMAObject(), "obj must be js object"); - - // Let C be Get(O, "constructor"). - JSHandle contructorKey = globalConst->GetHandledConstructorString(); - JSHandle objConstructor(JSTaggedValue::GetProperty(thread, JSHandle(obj), - contructorKey).GetValue()); - // ReturnIfAbrupt(C). - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - return SlowSpeciesConstructor(thread, objConstructor, defaultConstructor); -} - -JSHandle JSObject::SlowSpeciesConstructor(JSThread *thread, - const JSHandle &objConstructor, - const JSHandle &defaultConstructor) -{ - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - if (objConstructor->IsUndefined()) { - return defaultConstructor; - } - // If Type(C) is not Object, throw a TypeError exception. - if (!objConstructor->IsECMAObject()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is not Object", - JSHandle(thread, JSTaggedValue::Exception())); - } - // Let S be Get(C, @@species). - JSHandle speciesSymbol = env->GetSpeciesSymbol(); - JSHandle speciesConstructor = GetProperty(thread, objConstructor, speciesSymbol).GetValue(); - // ReturnIfAbrupt(S). - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - // If S is either undefined or null, return defaultConstructor. - if (speciesConstructor->IsUndefined() || speciesConstructor->IsNull()) { - return defaultConstructor; - } - // If IsConstructor(S) is true, return S. - if (speciesConstructor->IsConstructor()) { - return speciesConstructor; - } - // Throw a TypeError exception. - THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Constructor", - JSHandle(thread, JSTaggedValue::Exception())); - return JSHandle(thread, JSTaggedValue::Exception()); -} - -// 6.2.4.6 CompletePropertyDescriptor ( Desc ) -void PropertyDescriptor::CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc) -{ - // 1. ReturnIfAbrupt(Desc). - // 2. Assert: Desc is a Property Descriptor - // 3. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined, - // [[Enumerable]]: false, [[Configurable]]: false}. - // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then - if (!desc.IsAccessorDescriptor()) { - // a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]]. - // b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]]. - if (!desc.HasValue()) { - desc.SetValue(JSHandle(thread, JSTaggedValue::Undefined())); - } - if (!desc.HasWritable()) { - desc.SetWritable(false); - } - } else { - // a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]]. - // b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]]. - // Default value of Get and Set is undefined. - } - // 6. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]]. - // 7. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]]. - if (!desc.HasEnumerable()) { - desc.SetEnumerable(false); - } - if (!desc.HasConfigurable()) { - desc.SetConfigurable(false); - } -} - -// static -// When receiver has no elements and there is no enum cache and elements on receiver's prototype chain, -// the enum cache is a simple enum cache. -// When receiver and receiver's prototype chain have no elements, and the prototype is not modified, -// the enum cache is a enum cache with protochain -bool JSObject::IsSimpleEnumCacheValid(JSTaggedValue receiver) -{ - DISALLOW_GARBAGE_COLLECTION; - uint32_t numOfElements = JSObject::Cast(receiver.GetTaggedObject())->GetNumberOfElements(); - if (numOfElements > 0) { - return false; - } - - JSTaggedValue current = JSObject::GetPrototype(receiver); - while (current.IsHeapObject()) { - JSObject *currentObj = JSObject::Cast(current.GetTaggedObject()); - uint32_t numOfCurrentElements = currentObj->GetNumberOfElements(); - if (numOfCurrentElements > 0) { - return false; - } - JSHClass *hclass = currentObj->GetJSHClass(); - JSTaggedValue protoEnumCache = hclass->GetEnumCache(); - if (!protoEnumCache.IsUndefined()) { - return false; - } - current = JSObject::GetPrototype(current); - } - return true; -} - -bool JSObject::IsEnumCacheWithProtoChainInfoValid(JSTaggedValue receiver) -{ - DISALLOW_GARBAGE_COLLECTION; - // check elements of receiver - uint32_t numOfElements = JSObject::Cast(receiver.GetTaggedObject())->GetNumberOfElements(); - if (numOfElements > 0) { - return false; - } - // check protochain keys - JSTaggedValue proto = JSObject::GetPrototype(receiver); - if (!proto.IsECMAObject()) { - return false; - } - JSTaggedValue protoChangeMarker = proto.GetTaggedObject()->GetClass()->GetProtoChangeMarker(); - if (!protoChangeMarker.IsProtoChangeMarker()) { - return false; - } - if (ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())->GetHasChanged()) { - return false; - } - // check protochain elements - JSTaggedValue current = proto; - while (current.IsHeapObject()) { - JSObject *currentObj = JSObject::Cast(current.GetTaggedObject()); - uint32_t numOfCurrentElements = currentObj->GetNumberOfElements(); - if (numOfCurrentElements > 0) { - return false; - } - current = JSObject::GetPrototype(current); - } - return true; -} - -JSTaggedValue JSObject::TryGetEnumCache(JSThread *thread, JSTaggedValue obj) -{ - if (obj.IsSlowKeysObject() || obj.GetTaggedObject()->GetClass()->IsDictionaryMode()) { - return JSTaggedValue::Undefined(); - } - JSTaggedValue enumCache = obj.GetTaggedObject()->GetClass()->GetEnumCache(); - EnumCacheKind kind = JSObject::GetEnumCacheKind(thread, enumCache); - bool isEnumCacheValid = false; - switch (kind) { - case EnumCacheKind::SIMPLE: - isEnumCacheValid = IsSimpleEnumCacheValid(obj); - break; - case EnumCacheKind::PROTOCHAIN: - isEnumCacheValid = IsEnumCacheWithProtoChainInfoValid(obj); - break; - default: - break; - } - if (!isEnumCacheValid) { - return JSTaggedValue::Undefined(); - } - return enumCache; -} - -// 13.7.5.15 EnumerateObjectProperties ( O ) -JSHandle JSObject::EnumerateObjectProperties(JSThread *thread, const JSHandle &obj) -{ - JSHandle object; - if (obj->IsString()) { - JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - object = JSHandle::Cast(JSPrimitiveRef::StringCreate(thread, obj, undefined)); - } else { - object = JSTaggedValue::ToPrototypeOrObj(thread, obj); - } - - JSMutableHandle keys(thread, JSTaggedValue::Undefined()); - JSMutableHandle cachedHclass(thread, JSTaggedValue::Undefined()); - if (object->IsNull() || object->IsUndefined() || object->IsJSNativePointer()) { - JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - keys.Update(factory->EmptyArray()); - return factory->NewJSForinIterator(undefined, keys, cachedHclass); - } - keys.Update(TryGetEnumCache(thread, object.GetTaggedValue())); - if (!keys->IsUndefined()) { - cachedHclass.Update(JSTaggedValue(JSHandle::Cast(object)->GetJSHClass())); - return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object, keys, cachedHclass); - } - return LoadEnumerateProperties(thread, object); -} - -JSHandle JSObject::LoadEnumerateProperties(JSThread *thread, const JSHandle &object) -{ - PropertyAccessor accessor(thread, object); - JSHandle fastKeys = accessor.GetKeysFast(); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSForInIterator, thread); - JSMutableHandle keys(thread, JSTaggedValue::Undefined()); - JSMutableHandle cachedHclass(thread, JSTaggedValue::Undefined()); - if (fastKeys->IsUndefined()) { - keys.Update(accessor.GetKeysSlow()); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSForInIterator, thread); - } else { - keys.Update(fastKeys); - cachedHclass.Update(accessor.GetCachedHclass()); - } - return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object, keys, cachedHclass); -} - -void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, - const JSHandle &key, const JSHandle &value, - bool useForClass) -{ - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - PropertyAttributes attr = useForClass ? PropertyAttributes::Default(true, false, true) - : PropertyAttributes::Default(); - - if (value->IsAccessorData()) { - attr.SetIsAccessor(true); - } - - uint32_t index = 0; - if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { - AddElementInternal(thread, obj, index, value, attr); - return; - } - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); -} - -void JSObject::DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const JSHandle &value) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - ObjectOperator op(thread, obj, key, OperatorType::OWN); - ASSERT(op.IsFound()); - op.DefineSetter(value); -} - -void JSObject::DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const JSHandle &value) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - ObjectOperator op(thread, obj, key, OperatorType::OWN); - ASSERT(op.IsFound()); - op.DefineGetter(value); -} - -JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle &properties, - JSTaggedValue ihcVal) -{ - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - size_t length = properties->GetLength(); - uint32_t propsLen = 0; - for (size_t i = 0; i < length; i += 2) { // 2: skip a pair of key and value - if (properties->Get(i).IsHole()) { - break; - } - propsLen++; - } - if (propsLen <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY) { - JSHandle hclass; - bool isLiteral = false; - if (ihcVal.IsJSHClass()) { - hclass = JSHandle(thread, ihcVal); - } else { - hclass = factory->GetObjectLiteralHClass(properties, propsLen); - isLiteral = true; - } - if (hclass->IsTS()) { - if (CheckPropertiesForRep(properties, propsLen, hclass)) { - return CreateObjectFromPropertiesByIHClass(thread, properties, propsLen, hclass); - } else if (!isLiteral) { - hclass = factory->GetObjectLiteralHClass(properties, propsLen); - // if check failed, get literal object again - return CreateObjectFromPropertiesByIHClass(thread, properties, propsLen, hclass); - } - } - return CreateObjectFromProperties(thread, hclass, properties, propsLen); - } else { - JSHandle obj = factory->NewEmptyJSObject(0); // 0: no inline field - ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); - JSHClass::TransitionToDictionary(thread, obj); - JSObject::TryMigrateToGenericKindForJSObject(thread, obj, oldKind); - - JSMutableHandle dict( - thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propsLen))); - JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); - JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); - for (size_t i = 0; i < propsLen; i++) { - PropertyAttributes attr = PropertyAttributes::Default(); - // 2: literal contains a pair of key-value - valueHandle.Update(properties->Get(i * 2 + 1)); - // 2: literal contains a pair of key-value - keyHandle.Update(properties->Get(i * 2)); - JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); - dict.Update(newDict); - } - obj->SetProperties(thread, dict); - return obj; - } -} - -JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, - const JSHandle &hclass, - const JSHandle &properties, - uint32_t propsLen) -{ - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle obj = factory->NewOldSpaceObjLiteralByHClass(hclass); - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - - if (thread->IsPGOProfilerEnable()) { - // PGO need to track TrackType - JSHClass *oldHC = obj->GetJSHClass(); - LayoutInfo *layoutInfo = LayoutInfo::Cast(oldHC->GetLayout().GetTaggedObject()); - for (size_t i = 0; i < propsLen; i++) { - auto value = properties->Get(i * 2 + 1); - auto attr = layoutInfo->GetAttr(i); - if (attr.UpdateTrackType(value) && !oldHC->IsJSShared()) { - layoutInfo->SetNormalAttr(thread, i, attr); - } - obj->SetPropertyInlinedProps(thread, i, value); - } - } else { - for (size_t i = 0; i < propsLen; i++) { - // 2: literal contains a pair of key-value - obj->SetPropertyInlinedProps(thread, i, properties->Get(i * 2 + 1)); - } - } - return obj; -} - -JSHandle JSObject::CreateObjectFromPropertiesByIHClass(const JSThread *thread, - const JSHandle &properties, - uint32_t propsLen, - const JSHandle &ihc) -{ - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - - JSHandle obj = factory->NewOldSpaceObjLiteralByHClass(ihc); - ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - auto layout = LayoutInfo::Cast(ihc->GetLayout().GetTaggedObject()); - for (size_t i = 0; i < propsLen; i++) { - auto attr = layout->GetAttr(i); - auto value = JSObject::ConvertValueWithRep(attr, properties->Get(i * 2 + 1)); - ASSERT(value.first); - obj->SetPropertyInlinedPropsWithRep(thread, i, value.second); - } - return obj; -} - -bool JSObject::CheckPropertiesForRep( - const JSHandle &properties, uint32_t propsLen, const JSHandle &ihc) -{ - auto layout = LayoutInfo::Cast(ihc->GetLayout().GetTaggedObject()); - for (size_t i = 0; i < propsLen; i++) { - auto attr = layout->GetAttr(i); - auto value = JSObject::ConvertValueWithRep(attr, properties->Get(i * 2 + 1)); - // If value.first is false, indicating that value cannot be converted to the expected value of - // representation. For example, the representation is INT, but the value type is string. - if (!value.first) { - return false; - } - } - return true; -} - -void JSObject::AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, - const JSHandle &value, PropertyAttributes attr) -{ - ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); - ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - ASSERT_PRINT(attr.IsAccessor(), "Attr is not AccessorData"); - ObjectOperator op(thread, obj, key, OperatorType::OWN); - ASSERT(!op.IsFound()); - op.AddProperty(JSHandle::Cast(obj), JSHandle(value), attr); -} - -bool JSObject::UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value) -{ - [[maybe_unused]] DisallowGarbageCollection noGc; - NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject()); - int entry = dict->FindEntry(key); - if (entry == -1) { - return false; - } - dict->UpdateValue(thread, entry, value); - return true; -} - -void JSObject::TrimInlinePropsSpace(const JSThread *thread, const JSHandle &object, - uint32_t numberInlinedProps) -{ - if (numberInlinedProps > 0) { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - uint32_t newSize = object->GetClass()->GetObjectSize(); - size_t trimBytes = numberInlinedProps * JSTaggedValue::TaggedTypeSize(); - factory->FillFreeObject(ToUintPtr(*object) + newSize, trimBytes, RemoveSlots::YES, ToUintPtr(*object)); - } -} - -// The hash field may be a hash value, FunctionExtraInfo(JSNativePointer) or TaggedArray -void ECMAObject::SetHash(const JSThread *thread, int32_t hash, const JSHandle &obj) -{ - JSTaggedType hashField = Barriers::GetValue(*obj, HASH_OFFSET); - JSTaggedValue value(hashField); - if (value.IsHeapObject()) { - // Hash position reserve in advance. - if (value.IsTaggedArray()) { - TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject()); - array->Set(thread, array->GetExtraLength() + HASH_INDEX, JSTaggedValue(hash)); - } else if (value.IsNativePointer()) { // FunctionExtraInfo - JSHandle nativePointer(thread, value); - JSHandle newArray = - thread->GetEcmaVM()->GetFactory()->NewTaggedArray(RESOLVED_MAX_SIZE); - newArray->SetExtraLength(0); - newArray->Set(thread, HASH_INDEX, JSTaggedValue(hash)); - newArray->Set(thread, FUNCTION_EXTRA_INDEX, nativePointer.GetTaggedValue()); - Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); - } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); - } - } else { - Barriers::SetPrimitive(*obj, HASH_OFFSET, JSTaggedValue(hash).GetRawData()); - } -} - -int32_t ECMAObject::GetHash() const -{ - JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); - JSTaggedValue value(hashField); - if (value.IsHeapObject()) { - if (value.IsTaggedArray()) { - TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject()); - return array->Get(array->GetExtraLength() + HASH_INDEX).GetInt(); - } else { - // Default is 0 - return 0; - } - } - return value.GetInt(); -} - -bool ECMAObject::HasHash() const -{ - JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); - JSTaggedValue value(hashField); - if (value.IsInt() && value.GetInt() == 0) { - return false; - } - return true; -} - -void *ECMAObject::GetNativePointerField(int32_t index) const -{ - JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); - JSTaggedValue value(hashField); - if (value.IsTaggedArray()) { - auto array = TaggedArray::Cast(value); - if (static_cast(array->GetExtraLength()) > index) { - auto jsValue = array->Get(index); - if (UNLIKELY(!jsValue.IsJSNativePointer())) { - LOG_FULL(ERROR) << "jsValue is not js native pointer"; - return nullptr; - } - auto pointer = JSNativePointer::Cast(jsValue.GetTaggedObject()); - return pointer->GetExternalPointer(); - } - } - return nullptr; -} - -void ECMAObject::SetNativePointerField(const JSThread *thread, int32_t index, void *nativePointer, - const NativePointerCallback &callBack, void *data, size_t nativeBindingsize, Concurrent isConcurrent) -{ - JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); - JSTaggedValue value(hashField); - if (value.IsTaggedArray()) { - JSHandle array(thread, value); - if (static_cast(array->GetExtraLength()) > index) { - EcmaVM *vm = thread->GetEcmaVM(); - JSHandle current = JSHandle(thread, array->Get(thread, index)); - JSHandle obj(thread, this); - if (!current->IsHole() && nativePointer == nullptr) { - // Try to remove native pointer if exists. - vm->RemoveFromNativePointerList(*JSHandle(current)); - array->Set(thread, index, JSTaggedValue::Hole()); - } else if (obj->IsJSShared()) { - JSHandle pointer = - vm->GetFactory()->NewSJSNativePointer(nativePointer, callBack, data, false, nativeBindingsize); - array->Set(thread, index, pointer.GetTaggedValue()); - } else { - JSHandle pointer = vm->GetFactory()->NewJSNativePointer( - nativePointer, callBack, data, false, nativeBindingsize, isConcurrent); - array->Set(thread, index, pointer.GetTaggedValue()); - } - } - } -} - -int32_t ECMAObject::GetNativePointerFieldCount() const -{ - int32_t len = 0; - JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); - JSTaggedValue value(hashField); - if (value.IsTaggedArray()) { - TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject()); - len = static_cast(array->GetExtraLength()); - } - return len; -} - -void ECMAObject::SetNativePointerFieldCount(const JSThread *thread, int32_t count) -{ - if (count == 0) { - return; - } - JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); - JSHandle value(thread, JSTaggedValue(hashField)); - JSHandle obj(thread, this); - JSHandle object(obj); - bool isShared = object->IsJSShared(); - if (value->IsHeapObject()) { - if (value->IsTaggedArray()) { - JSHandle array(value); - // Native Pointer field count is fixed. - if (array->GetExtraLength() == 0) { - JSHandle newArray = - isShared ? thread->GetEcmaVM()->GetFactory()->NewSTaggedArray(count + RESOLVED_MAX_SIZE) - : thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE); - newArray->SetExtraLength(count); - newArray->Set(thread, count + HASH_INDEX, array->Get(HASH_INDEX)); - newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, array->Get(FUNCTION_EXTRA_INDEX)); - Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); - } - } else if (value->IsJSNativePointer()) { - JSHandle newArray = - isShared ? thread->GetEcmaVM()->GetFactory()->NewSTaggedArray(count + RESOLVED_MAX_SIZE) - : thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE); - newArray->SetExtraLength(count); - newArray->Set(thread, count + HASH_INDEX, JSTaggedValue(0)); - newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, value); - Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); - } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); - } - } else { - JSHandle newArray = isShared ? thread->GetEcmaVM()->GetFactory()->NewSTaggedArray(count + 1) - : thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + 1); - newArray->SetExtraLength(count); - newArray->Set(thread, count + HASH_INDEX, value); - Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); - } -} - -bool JSObject::ElementsAndPropertiesIsEmpty() const -{ - if (TaggedArray::Cast(GetElements().GetTaggedObject())->GetLength() == 0 && - TaggedArray::Cast(GetProperties().GetTaggedObject())->GetLength() == 0) { - return true; - } - return false; -} - -void JSObject::TryMigrateToGenericKindForJSObject(const JSThread *thread, const JSHandle &obj, - const ElementsKind oldKind) -{ - if (obj->IsJSArray() && HasMutantTaggedArrayElements(obj)) { - Elements::MigrateArrayWithKind(thread, obj, oldKind, ElementsKind::GENERIC); - } -} -} // namespace panda::ecmascript + { + if (UNLIKELY(accessor.IsInternal())) { + return accessor.CallInternalSet(thread, JSHandle::Cast(receiver), value, mayThrow); + } + JSTaggedValue setter = accessor.GetSetter(); + // 8. If setter is undefined, return false. + if (setter.IsUndefined()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false); + } + return false; + } + + JSHandle func(thread, setter); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 1); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + info->SetCallArg(value.GetTaggedValue()); + JSFunction::Call(info); + + // 10. ReturnIfAbrupt(setterResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + return true; + } + + JSTaggedValue JSObject::CallGetter(JSThread *thread, const AccessorData *accessor, + const JSHandle &receiver) + { + JSTaggedValue getter = accessor->GetGetter(); + // 7. If getter is undefined, return undefined. + if (getter.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + + JSHandle func(thread, getter); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 0); + JSTaggedValue res = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; + } + + // 9.1.8 [[Get]] (P, Receiver) + OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, receiver, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); + } + + OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); + } + + OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, SCheckMode sCheckMode) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + if (obj->IsJSSharedArray()) { + return JSSharedArray::GetProperty(thread, obj, key, sCheckMode); + } + ObjectOperator op(thread, obj, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); + } + + OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, uint32_t index) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + + ObjectOperator op(thread, obj, index); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); + } + + OperationResult JSObject::GetPropertyFromGlobal(JSThread *thread, const JSHandle &key) + { + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); + } + + PropertyBox* JSObject::GetGlobalPropertyBox(JSTaggedValue key) + { + ASSERT(IsJSGlobalObject()); + auto dict = GlobalDictionary::Cast(GetProperties().GetTaggedObject()); + auto entry = dict->FindEntry(key); + if (entry == -1) { + return nullptr; + } + return dict->GetBox(entry); + } + + PropertyBox* JSObject::GetGlobalPropertyBox(JSThread *thread, const std::string& key) + { + auto factory = thread->GetEcmaVM()->GetFactory(); + auto keyValue = factory->NewFromUtf8(key).GetTaggedValue(); + return GetGlobalPropertyBox(keyValue); + } + + JSTaggedValue JSObject::GetProperty(JSThread *thread, ObjectOperator *op) + { + JSHandle receiver = op->GetReceiver(); + JSHandle holder = op->GetHolder(); + if (receiver->IsNativeModuleFailureInfo()) { + JSTaggedValue failureInfo = JSHandle::Cast(receiver)->GetArkNativeModuleFailureInfo(); + THROW_REFERENCE_ERROR_AND_RETURN(thread, ConvertToString(failureInfo).c_str(), JSTaggedValue::Undefined()); + } + if (holder->IsJSProxy()) { + if (op->IsElement()) { + JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); + return JSProxy::GetProperty(thread, JSHandle::Cast(holder), key, receiver) + .GetValue() + .GetTaggedValue(); + } + return JSProxy::GetProperty(thread, JSHandle::Cast(holder), op->GetKey(), receiver) + .GetValue() + .GetTaggedValue(); + } + + // 4. If desc is undefined, then + if (!op->IsFound()) { + // 4c. If obj and parent is null, return undefined. + return JSTaggedValue::Undefined(); + } + // 5. If IsDataDescriptor(desc) is true, return desc.[[Value]] + JSTaggedValue ret = ShouldGetValueFromBox(op); + if (!op->IsAccessorDescriptor()) { + return ret; + } + + // 6. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]]. + AccessorData *accessor = AccessorData::Cast(ret.GetTaggedObject()); + // 8. Return Call(getter, Receiver). + if (UNLIKELY(accessor->IsInternal())) { + return accessor->CallInternalGet(thread, JSHandle::Cast(holder)); + } + return CallGetter(thread, accessor, receiver); + } + + bool JSObject::DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + SCheckMode sCheckMode) + { + // 1. Assert: IsPropertyKey(P) is true. + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 2. Let desc be O.[[GetOwnProperty]](P). + ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); + + // 4. If desc is undefined, return true. + if (!op.IsFound()) { + return true; + } + // 5. If desc.[[Configurable]] is true, then + // a. Remove the own property with name P from O. + // b. Return true. + // 6. Return false. + if (op.IsConfigurable() || sCheckMode == SCheckMode::SKIP) { + op.DeletePropertyInHolder(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + obj->GetClass()->SetHasDeleteProperty(true); + return true; + } + return false; + } + + bool JSObject::GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc) + { + return OrdinaryGetOwnProperty(thread, obj, key, desc); + } + + bool JSObject::GlobalGetOwnProperty(JSThread *thread, const JSHandle &key, PropertyDescriptor &desc) + { + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, key, OperatorType::OWN); + + if (!op.IsFound()) { + return false; + } + + op.ToPropertyDescriptor(desc); + + if (desc.HasValue()) { + PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject()); + JSHandle valueHandle(thread, cell->GetValue()); + desc.SetValue(valueHandle); + } + ASSERT(!desc.GetValue()->IsInternalAccessor()); + return true; + } + + bool JSObject::OrdinaryGetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) + { + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); + + if (!op.IsFound()) { + return false; + } + + op.ToPropertyDescriptor(desc); + + if (desc.HasValue() && obj->IsJSGlobalObject()) { + JSTaggedValue val = desc.GetValue().GetTaggedValue(); + if (val.IsPropertyBox()) { + PropertyBox *cell = PropertyBox::Cast(val.GetTaggedObject()); + JSHandle valueHandle(thread, cell->GetValue()); + desc.SetValue(valueHandle); + } + } + + return true; + } + + bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyDescriptor &desc, SCheckMode sCheckMode) + { + return OrdinaryDefineOwnProperty(thread, obj, key, desc, sCheckMode); + } + + bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc, SCheckMode sCheckMode) + { + return OrdinaryDefineOwnProperty(thread, obj, index, desc, sCheckMode); + } + + // 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc) + bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc, + SCheckMode sCheckMode) + { + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 1. Let current be O.[[GetOwnProperty]](P). + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, key, OperatorType::OWN); + + bool extensible = obj->IsExtensible(); + // make extensible for shared array to add element. + if (obj->IsJSSArray() && op.IsElement()) { + extensible = true; + } + PropertyDescriptor current(thread); + op.ToPropertyDescriptor(current); + // 4. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current, sCheckMode); + } + + bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc, SCheckMode sCheckMode) + { + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, index, OperatorType::OWN); + + bool extensible = obj->IsExtensible(); + PropertyDescriptor current(thread); + op.ToPropertyDescriptor(current); + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current, sCheckMode); + } + + bool JSObject::ValidateDataDescriptorWhenConfigurable(ObjectOperator *op, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t, SCheckMode sCheckMode) + { + // 8a i. Return false, if the [[Writable]] field of current is false and the [[Writable]] field of Desc + // is true. + if (!current.IsWritable() && desc.HasWritable() && desc.IsWritable()) { + return false; + } + // 8a ii. If the [[Writable]] field of current is false, then + if (!current.IsWritable()) { + if (desc.HasValue() && !JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) { + return false; + } + } + if (op->HasHolder() && op->GetHolder()->IsJSShared() && (sCheckMode == SCheckMode::CHECK)) { + if (!desc.HasValue()) { + THROW_TYPE_ERROR_AND_RETURN(op->GetThread(), GET_MESSAGE_STRING(UpdateSendableAttributes), false); + } + if (!ClassHelper::MatchFieldType(current.GetSharedFieldType(), desc.GetValue().GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(op->GetThread(), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), false); + } + } + return true; + } + + // 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current) + bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t, SCheckMode sCheckMode) + { + // 2. If current is undefined, then + if (current.IsEmpty()) { + // 2a. If extensible is false, return false. + if (!(extensible || (op->HasHolder() && op->GetHolder()->IsJSShared() && sCheckMode == SCheckMode::SKIP))) { + return false; + } + if (!op->HasHolder()) { + return true; + } + + // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then + PropertyAttributes attr(desc); + bool success = false; + if (!desc.IsAccessorDescriptor()) { + op->UpdateDetector(); + success = op->AddPropertyInHolder(desc.GetValue(), attr); + } else { // is AccessorDescriptor + // may GC in NewAccessorData, so we need to handle getter and setter. + JSThread *thread = op->GetThread(); + JSHandle accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData(); + if (desc.HasGetter()) { + accessor->SetGetter(thread, desc.GetGetter()); + } + + if (desc.HasSetter()) { + accessor->SetSetter(thread, desc.GetSetter()); + } + op->UpdateDetector(); + success = op->AddPropertyInHolder(JSHandle::Cast(accessor), attr); + } + + return success; + } + + // 3. Return true, if every field in Desc is absent + // 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the + // same value as the corresponding field in current when compared using the SameValue algorithm. + if ((!desc.HasEnumerable() || desc.IsEnumerable() == current.IsEnumerable()) && + (!desc.HasConfigurable() || desc.IsConfigurable() == current.IsConfigurable()) && + (!desc.HasValue() || JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) && + (!desc.HasWritable() || (current.IsWritable() == desc.IsWritable())) && + (!desc.HasGetter() || + (current.HasGetter() && JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter()))) && + (!desc.HasSetter() || + (current.HasSetter() && JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())))) { + return true; + } + + // 5. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // 5a. Return false, if the [[Configurable]] field of Desc is true. + if (desc.HasConfigurable() && desc.IsConfigurable()) { + return false; + } + // b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current + // and Desc are the Boolean negation of each other. + if (desc.HasEnumerable() && (desc.IsEnumerable() != current.IsEnumerable())) { + return false; + } + } + + // 6. If IsGenericDescriptor(Desc) is true, no further validation is required. + if (desc.IsGenericDescriptor()) { + // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then + } else if (current.IsDataDescriptor() != desc.IsDataDescriptor()) { + // 7a. Return false, if the [[Configurable]] field of current is false. + if (!current.IsConfigurable()) { + return false; + } + // 7b. If IsDataDescriptor(current) is true, then + if (current.IsDataDescriptor()) { + // 7bi. If O is not undefined, convert the property named P of object O from a data property to an + // accessor property. Preserve the existing values of the converted property’s [[Configurable]] and + // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. + } else { + // 7ci. If O is not undefined, convert the property named P of object O from an accessor property to a + // data property. Preserve the existing values of the converted property’s [[Configurable]] and + // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. + } + // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then + } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) { + // 8a. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable() && !ValidateDataDescriptorWhenConfigurable(op, desc, current, sCheckMode)) { + return false; + } + // 8b. Else the [[Configurable]] field of current is true, so any change is acceptable. + } else { // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true, + // 9a. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // i. Return false, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) + // is false. + if (desc.HasSetter() && !JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())) { + return false; + } + // ii. Return false, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], + // current.[[Get]]) is false. + if (desc.HasGetter() && !JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter())) { + return false; + } + } + } + + if (op->HasHolder()) { + // 10. If O is not undefined, then + // a. For each field of Desc that is present, set the corresponding attribute of the property named P of object + // O to the value of the field. + if (!desc.HasValue() && desc.HasWritable() && current.HasValue()) { + // [[Value]] and [[Writable]] attributes are set to the value of the corresponding field in Desc + // if Desc has that field or to the attribute's default value otherwise. + PropertyDescriptor newDesc = desc; + JSHandle valueHandle = current.GetValue(); + if (valueHandle->IsPropertyBox()) { + JSTaggedValue value = PropertyBox::Cast(valueHandle->GetTaggedObject())->GetValue(); + valueHandle = JSHandle(op->GetThread(), value); + } + newDesc.SetValue(valueHandle); + op->UpdateDetector(); + return op->WriteDataPropertyInHolder(newDesc); + } + op->UpdateDetector(); + return op->WriteDataPropertyInHolder(desc); + } + return true; + } + + // 9.1.6.2 IsCompatiblePropertyDescriptor (Extensible, Desc, Current) + bool JSObject::IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t) + { + // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current). + ObjectOperator op; + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); + } + + JSTaggedValue JSObject::GetPrototype(const JSHandle &obj) + { + JSHClass *hclass = obj->GetJSHClass(); + return hclass->GetPrototype(); + } + + bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, + const JSHandle &proto, + bool isChangeProto) + { + ASSERT_PRINT(proto->IsECMAObject() || proto->IsNull(), "proto must be object or null"); + JSTaggedValue current = JSObject::GetPrototype(obj); + if (current == proto.GetTaggedValue()) { + return true; + } + if (!obj->IsExtensible()) { + return false; + } + bool done = false; + JSMutableHandle tempProtoHandle(thread, proto.GetTaggedValue()); + while (!done) { + if (tempProtoHandle->IsNull() || !tempProtoHandle->IsECMAObject()) { + done = true; + } else if (JSTaggedValue::SameValue(tempProtoHandle.GetTaggedValue(), obj.GetTaggedValue())) { + return false; + } else { + if (tempProtoHandle->IsJSProxy()) { + break; + } + tempProtoHandle.Update(JSTaggedValue::GetPrototype(thread, JSHandle(tempProtoHandle))); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + } + ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); + // map transition + JSHClass::SetPrototypeTransition(thread, obj, proto, isChangeProto); + TryMigrateToGenericKindForJSObject(thread, obj, oldKind); + return true; + } + + bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) + { + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, key); + + JSHandle holder = op.GetHolder(); + if (holder->IsJSProxy()) { + return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); + } + + return op.IsFound(); + } + + bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, uint32_t index) + { + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, index); + + JSHandle holder = op.GetHolder(); + if (holder->IsJSProxy()) { + JSHandle key(thread, JSTaggedValue(index)); + return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); + } + + return op.IsFound(); + } + + bool JSObject::PreventExtensions(JSThread *thread, const JSHandle &obj) + { + if (obj->IsExtensible()) { + JSHandle jshclass(thread, obj->GetJSHClass()); + JSHandle newHclass = JSHClass::TransitionExtension(thread, jshclass); + #if ECMASCRIPT_ENABLE_IC + JSHClass::NotifyHclassChanged(thread, jshclass, newHclass); + #endif + ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); + JSHClass::RestoreElementsKindToGeneric(*newHclass); + obj->SynchronizedSetClass(thread, *newHclass); + TryMigrateToGenericKindForJSObject(thread, obj, oldKind); + } + + return true; + } + + // 9.1.12 [[OwnPropertyKeys]] ( ) + JSHandle JSObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj) + { + uint32_t numOfElements = obj->GetNumberOfElements(); + uint32_t keyLen = numOfElements + obj->GetNumberOfKeys(); + + JSHandle keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen); + + if (numOfElements > 0) { + GetAllElementKeys(thread, obj, 0, keyArray); + } + GetAllKeys(thread, obj, static_cast(numOfElements), keyArray); + return keyArray; + } + + JSHandle JSObject::GetAllPropertyKeys(JSThread *thread, const JSHandle &obj, uint32_t filter) + { + JSMutableHandle currentObj(thread, obj); + JSMutableHandle currentObjValue(thread, currentObj); + + uint32_t curObjNumberOfElements = currentObj->GetNumberOfElements(); + uint32_t curObjNumberOfKeys = currentObj->GetNumberOfKeys(); + uint32_t curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys; + uint32_t retArrayLength = curObjectKeysLength; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle retArray(thread, factory->NewTaggedArray(retArrayLength)); + uint32_t retArrayEffectivelength = 0; + + do { + curObjNumberOfElements = currentObj->GetNumberOfElements(); + curObjNumberOfKeys = currentObj->GetNumberOfKeys(); + curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys; + uint32_t minRequireLength = curObjectKeysLength + retArrayEffectivelength; + if (retArrayLength < minRequireLength) { + // expand retArray + if (retArrayLength != 0) { + retArray.Update(factory->NewAndCopyTaggedArray(retArray, minRequireLength, retArrayLength)); + } else { + retArray.Update(factory->NewTaggedArray(minRequireLength)); + } + retArrayLength = minRequireLength; + } + + GetAllElementKeysByFilter(thread, currentObj, retArray, retArrayEffectivelength, filter); + + GetAllKeysByFilter(thread, currentObj, retArrayEffectivelength, retArray, filter); + bool isInculdePrototypes = (filter & NATIVE_KEY_INCLUDE_PROTOTYPES); + if (!isInculdePrototypes) { + break; + } + currentObj.Update(GetPrototype(currentObj)); + currentObjValue.Update(currentObj); + } while (currentObjValue->IsHeapObject()); + + if (retArrayEffectivelength == 0 && (filter & NATIVE_KEY_OWN_ONLY)) { + return retArray; + } + JSMutableHandle element(thread, JSTaggedValue::Undefined()); + if (filter & NATIVE_KEY_NUMBERS_TO_STRINGS) { + for (uint32_t i = 0; i < retArrayEffectivelength; i++) { + element.Update(retArray->Get(i)); + if (element->IsNumber()) { + retArray->Set(thread, i, base::NumberHelper::NumberToString(thread, + JSTaggedValue(element->GetNumber()))); + } + } + } + uint32_t elementIndex = 0; + if (filter & NATIVE_KEY_SKIP_STRINGS) { + while ((retArrayEffectivelength > 0) && (elementIndex < retArrayEffectivelength)) { + if (retArray->Get(elementIndex).IsString()) { + TaggedArray::RemoveElementByIndex(thread, retArray, elementIndex, retArrayEffectivelength); + retArrayEffectivelength--; + } else { + elementIndex++; + } + } + } + if (retArray->GetLength() > retArrayEffectivelength) { + retArray->Trim(thread, retArrayEffectivelength); + } + return retArray; + } + + void JSObject::CollectEnumKeysAlongProtoChain(JSThread *thread, const JSHandle &obj, + JSHandle keyArray, uint32_t *keys, + JSHandle shadowQueue, int32_t lastLength) + { + ASSERT(!obj->IsJSGlobalObject()); + + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + JSHClass *jsHclass = obj->GetJSHClass(); + int end = static_cast(jsHclass->NumberOfProps()); + if (end > 0) { + LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()) + ->GetAllEnumKeys(thread, end, *keys, keyArray, keys, shadowQueue, obj, lastLength); + } + return; + } + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllEnumKeys(thread, *keys, keyArray, keys, shadowQueue, lastLength); + } + + void JSObject::AppendOwnEnumPropertyKeys(JSThread *thread, const JSHandle &obj, + JSHandle keyArray, uint32_t *keys, + JSHandle shadowQueue) + { + int32_t lastLength = *keys; + uint32_t numOfElements = obj->GetNumberOfElements(); + if (numOfElements > 0) { + CollectEnumElementsAlongProtoChain(thread, obj, *keys, keyArray, keys, lastLength); + } + CollectEnumKeysAlongProtoChain(thread, obj, keyArray, keys, shadowQueue, lastLength); + } + + JSHandle JSObject::GetOwnEnumPropertyKeys(JSThread *thread, const JSHandle &obj) + { + uint32_t numOfElements = obj->GetNumberOfElements(); + uint32_t keyLen = numOfElements + obj->GetNumberOfKeys(); + + JSHandle keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen); + + if (numOfElements > 0) { + GetEnumElementKeys(thread, obj, 0, keyArray); + } + GetAllEnumKeys(thread, obj, static_cast(numOfElements), keyArray); + return keyArray; + } + + JSHandle JSObject::ObjectCreate(JSThread *thread, const JSHandle &proto) + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor(env->GetObjectFunction()); + JSHandle objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(constructor); + SetPrototype(thread, objHandle, JSHandle(proto)); + return objHandle; + } + + // 7.3.4 CreateDataProperty (O, P, V) + bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, SCheckMode sCheckMode) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + if (!JSHandle::Cast(obj)->IsJSShared()) { + sCheckMode = SCheckMode::CHECK; + } + auto result = ObjectFastOperator::SetPropertyByValue( + thread, obj.GetTaggedValue(), key.GetTaggedValue(), value.GetTaggedValue(), sCheckMode); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!result.IsHole()) { + return !result.IsException(); + } + PropertyDescriptor desc(thread, value, true, true, true); + return JSTaggedValue::DefineOwnProperty(thread, JSHandle::Cast(obj), key, desc, sCheckMode); + } + + bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value, SCheckMode sCheckMode) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + auto result = ObjectFastOperator::SetPropertyByIndex + (thread, obj.GetTaggedValue(), index, value.GetTaggedValue()); + if (!result.IsHole()) { + return !result.IsException(); + } + PropertyDescriptor desc(thread, value, true, true, true); + return DefineOwnProperty(thread, obj, index, desc, sCheckMode); + } + + // 7.3.5 CreateMethodProperty (O, P, V) + bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, + SCheckMode sCheckMode) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + bool success = CreateDataProperty(thread, obj, key, value, sCheckMode); + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); + } + return success; + } + + bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value, SCheckMode sCheckMode) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + + bool success = CreateDataProperty(thread, obj, index, value, sCheckMode); + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); + } + return success; + } + // 7.3.6 CreateDataPropertyOrThrow (O, P, V) + bool JSObject::CreateMethodProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + PropertyDescriptor desc(thread, value, true, false, true); + return DefineOwnProperty(thread, obj, key, desc); + } + + JSHandle JSObject::CallFunction(JSThread *thread, const JSHandle &func) + { + if (func->IsUndefined() || func->IsNull()) { + return JSHandle(thread, JSTaggedValue::Undefined()); + } + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", func); + } + return func; + } + + // 7.3.9 GetMethod (O, P) + JSHandle JSObject::GetMethod(JSThread *thread, const JSHandle &obj, + const JSHandle &key) + { + JSHandle func = JSTaggedValue::GetProperty(thread, obj, key).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + func = CallFunction(thread, func); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + return func; + } + + JSHandle JSObject::FastGetMethod(JSThread *thread, const JSHandle &obj, + const JSHandle &key) + { + JSHandle func(thread, ObjectFastOperator::FastGetPropertyByName(thread, obj.GetTaggedValue(), + key.GetTaggedValue())); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + func = CallFunction(thread, func); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + return func; + } + + // 7.3.14 SetIntegrityLevel (O, level) + bool JSObject::SetIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), + "level is not a valid IntegrityLevel"); + + bool status = JSTaggedValue::PreventExtensions(thread, JSHandle(obj)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!status) { + return false; + } + + JSHandle jshandleKeys = + JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + PropertyDescriptor descNoConf(thread); + descNoConf.SetConfigurable(false); + PropertyDescriptor descNoConfWrite(thread); + descNoConfWrite.SetWritable(false); + descNoConfWrite.SetConfigurable(false); + + if (level == IntegrityLevel::SEALED) { + uint32_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (uint32_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + [[maybe_unused]] bool success = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, descNoConf); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + } else { + uint32_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (uint32_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + PropertyDescriptor currentDesc(thread); + bool curDescStatus = + JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), handleKey, currentDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (curDescStatus) { + PropertyDescriptor desc = currentDesc.IsAccessorDescriptor() ? descNoConf : descNoConfWrite; + [[maybe_unused]] bool success = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, desc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + } + } + return true; + } + + bool JSObject::FreezeSharedObject(JSThread *thread, const JSHandle &obj) + { + ASSERT_PRINT(JSHandle(obj)->IsJSSharedObject() || + JSHandle(obj)->IsJSSharedFunction() || + JSHandle(obj)->IsJSSharedAsyncFunction(), + "Obj is not a valid shared object"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // It is not extensible for shared object. + if (obj->IsExtensible()) { + return false; + } + JSHandle hclass(thread, obj->GetClass()); + auto newClass = JSHClass::Clone(thread, hclass); + if (!hclass->IsDictionaryMode()) { + uint32_t propNumber = hclass->NumberOfProps(); + JSHandle layoutInfo(thread, hclass->GetLayout()); + JSHandle newLayoutInfo = factory->CreateSLayoutInfo(propNumber); + for (uint32_t i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfo->GetKey(i); + PropertyAttributes attr = layoutInfo->GetAttr(i); + attr.SetWritable(false); + newLayoutInfo->AddKey(thread, i, key, attr); + } + newClass->SetLayout(thread, newLayoutInfo); + obj->SynchronizedSetClass(thread, *newClass); + } else { + auto dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->UpdateAllAttributesToNoWitable(thread); + } + return true; + } + + // 7.3.15 TestIntegrityLevel (O, level) + bool JSObject::TestIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), + "level is not a valid IntegrityLevel"); + + bool status = JSHandle(obj)->IsExtensible(thread); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (status) { + return false; + } + + JSHandle jshandleKeys = + JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + uint32_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (uint32_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + PropertyDescriptor currentDesc(thread); + bool curDescStatus = + JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), handleKey, currentDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (curDescStatus) { + if (currentDesc.IsConfigurable()) { + return false; + } + if (level == IntegrityLevel::FROZEN && + currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) { + return false; + } + } + } + return true; + } + + // 7.3.21 EnumerableOwnNames (O) + JSHandle JSObject::EnumerableOwnNames(JSThread *thread, const JSHandle &obj) + { + ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle tagObj(obj); + // fast mode + if (tagObj->IsJSObject() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) { + uint32_t copyLengthOfKeys = 0; + uint32_t copyLengthOfElements = 0; + auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, ©LengthOfKeys, ©LengthOfElements); + JSHandle keyArray = keyElementPair.first; + JSHandle elementArray = keyElementPair.second; + JSHandle keys; + if (copyLengthOfKeys != 0 && copyLengthOfElements != 0) { + keys = TaggedArray::AppendSkipHole(thread, elementArray, keyArray, copyLengthOfKeys + copyLengthOfElements); + } else if (copyLengthOfKeys != 0) { + if (copyLengthOfKeys < keyArray->GetLength()) { + // keyArray will skip nonEnumerable properties, need re-set length. + keyArray->Trim(thread, copyLengthOfKeys); + } + return keyArray; + } else if (copyLengthOfElements != 0) { + if (copyLengthOfElements < elementArray->GetLength()) { + // elementArray will skip hole value, need re-set length. + elementArray->Trim(thread, copyLengthOfElements); + } + return elementArray; + } else { + keys = factory->EmptyArray(); + } + return keys; + } + + uint32_t copyLength = 0; + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + uint32_t length = keys->GetLength(); + + JSHandle names = factory->NewTaggedArray(length); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < length; i++) { + keyHandle.Update(keys->Get(i)); + if (keyHandle->IsString()) { + PropertyDescriptor desc(thread); + bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), + keyHandle, desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (status && desc.IsEnumerable()) { + names->Set(thread, copyLength, keyHandle); + copyLength++; + } + } + } + + return factory->CopyArray(names, length, copyLength); + } + + void JSObject::EnumerableOwnPropertyNamesHelper(JSThread *thread, const JSHandle &obj, + const JSHandle &arr, JSHandle &prop, uint32_t &index, bool &fastMode, PropertyKind kind) + { + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSHandle objClass(thread, obj->GetJSHClass()); + uint32_t length = arr->GetLength(); + for (uint32_t i = 0; i < length; i++) { + key.Update(arr->Get(thread, i)); + if (!JSTaggedValue::IsPropertyKey(key)) { + break; + } + JSTaggedValue value = JSTaggedValue::Hole(); + if (fastMode) { + value = ObjectFastOperator::GetPropertyByValue + (thread, obj.GetTaggedValue(), key.GetTaggedValue()); + RETURN_IF_ABRUPT_COMPLETION(thread); + } + if (value.IsHole()) { + PropertyDescriptor desc(thread); + bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), key, desc); + RETURN_IF_ABRUPT_COMPLETION(thread); + if (!status || !desc.IsEnumerable()) { + continue; + } + if (desc.HasValue()) { + value = desc.GetValue().GetTaggedValue(); + } else { + OperationResult opResult = JSTaggedValue::GetProperty(thread, JSHandle::Cast(obj), key); + RETURN_IF_ABRUPT_COMPLETION(thread); + value = opResult.GetValue().GetTaggedValue(); + } + } + index = SetValuesOrEntries(thread, prop, index, key, JSHandle(thread, value), kind); + fastMode = fastMode ? CheckHClassHit(obj, objClass) : fastMode; + } + } + + JSHandle JSObject::EnumerableOwnPropertyNames(JSThread *thread, const JSHandle &obj, + PropertyKind kind) + { + // 1. Assert: Type(O) is Object. + ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); + + // 2. Let ownKeys be ? O.[[OwnPropertyKeys]](). + JSHandle tagObj(obj); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (tagObj->IsJSObject() && !tagObj->IsJSProxy() && !tagObj->IsTypedArray() && !tagObj->IsModuleNamespace()) { + uint32_t copyLengthOfKeys = 0; + uint32_t copyLengthOfElements = 0; + uint32_t index = 0; + bool fastMode = true; + auto keyElementPair = GetOwnEnumerableNamesInFastMode(thread, obj, ©LengthOfKeys, ©LengthOfElements); + JSHandle keyArray = keyElementPair.first; + JSHandle elementArray = keyElementPair.second; + JSHandle properties = factory->NewTaggedArray(copyLengthOfKeys + copyLengthOfElements); + if (copyLengthOfElements != 0) { + EnumerableOwnPropertyNamesHelper(thread, obj, elementArray, properties, index, fastMode, kind); + } + if (copyLengthOfKeys != 0) { + EnumerableOwnPropertyNamesHelper(thread, obj, keyArray, properties, index, fastMode, kind); + } + if (UNLIKELY(!fastMode && index < copyLengthOfKeys + copyLengthOfElements)) { + properties->Trim(thread, index); + } + return properties; + } + + JSHandle ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 3. Let properties be a new empty List. + uint32_t length = ownKeys->GetLength(); + JSHandle properties = factory->NewTaggedArray(length); + + // 4. For each element key of ownKeys, do + // a. If Type(key) is String, then + // i. Let desc be ? O.[[GetOwnProperty]](key). + // ii. If desc is not undefined and desc.[[Enumerable]] is true, then + // 1. If kind is key, append key to properties. + // 2. Else, + // a. Let value be ? Get(O, key). + // b. If kind is value, append value to properties. + // c. Else, + // i. Assert: kind is key+value. + // ii. Let entry be ! CreateArrayFromList(« key, value »). + // iii. Append entry to properties. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t index = 0; + for (uint32_t i = 0; i < length; i++) { + key.Update(ownKeys->Get(thread, i)); + if (key->IsString()) { + PropertyDescriptor desc(thread); + bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), + key, desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + if (status && desc.IsEnumerable()) { + if (kind == PropertyKind::KEY) { + properties->Set(thread, index++, key); + } else { + OperationResult result = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(obj), key); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSHandle value = result.GetValue(); + index = SetValuesOrEntries(thread, properties, index, key, value, kind); + } + } + } + } + + if (UNLIKELY(index < length)) { + properties->Trim(thread, index); + } + // 5. Return properties. + return properties; + } + + JSHandle JSObject::GetFunctionRealm(JSThread *thread, const JSHandle &object) + { + // 1. Assert: obj is a callable object. + ASSERT(object->IsCallable()); + // 2. If obj has a [[Realm]] internal slot, then return obj’s [[Realm]] internal slot. + // 3. If obj is a Bound Function exotic object, then + if (object->IsBoundFunction()) { + // a. Let target be obj’s [[BoundTargetFunction]] internal slot. + JSHandle target(thread, JSHandle(object)->GetBoundTarget()); + // b. Return GetFunctionRealm(target). + return GetFunctionRealm(thread, target); + } + // 4. If obj is a Proxy exotic object, then + if (object->IsJSProxy()) { + // a. If the value of the [[ProxyHandler]] internal slot of obj is null, throw a TypeError exception. + if (JSHandle(object)->GetHandler().IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSObject::GetFunctionRealm: handler is null", + JSHandle(thread, JSTaggedValue::Exception())); + } + // b. Let proxyTarget be the value of obj’s [[ProxyTarget]] internal slot. + JSHandle proxyTarget(thread, JSHandle(object)->GetTarget()); + return GetFunctionRealm(thread, proxyTarget); + } + + if (object->IsJSShared()) { + // LexicalEnv in sharedConstructor is constructor itself. And Shared Constructors shares the same GlobalEnv. + return thread->GetEcmaVM()->GetGlobalEnv(); + } + + JSTaggedValue maybeGlobalEnv = JSHandle(object)->GetLexicalEnv(); + while (!maybeGlobalEnv.IsJSGlobalEnv()) { + if (maybeGlobalEnv.IsUndefined()) { + return thread->GetEcmaVM()->GetGlobalEnv(); + } + maybeGlobalEnv = LexicalEnv::Cast(maybeGlobalEnv.GetTaggedObject())->GetParentEnv(); + } + return JSHandle(thread, maybeGlobalEnv); + } + + bool JSObject::InstanceOf(JSThread *thread, const JSHandle &object, + const JSHandle &target) + { + // 1. If Type(target) is not Object, throw a TypeError exception. + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when type of target is not Object", false); + } + + EcmaVM *vm = thread->GetEcmaVM(); + // 2. Let instOfHandler be GetMethod(target, @@hasInstance). + JSHandle instOfHandler = FastGetMethod(thread, target, vm->GetGlobalEnv()->GetHasInstanceSymbol()); + + // 3. ReturnIfAbrupt(instOfHandler). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 4. If instOfHandler is not undefined, then + if (!instOfHandler->IsUndefined()) { + // a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)). + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, instOfHandler, target, undefined, 1); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + info->SetCallArg(object.GetTaggedValue()); + JSTaggedValue tagged = JSFunction::Call(info); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return tagged.ToBoolean(); + } + + // 5. If IsCallable(target) is false, throw a TypeError exception. + if (!target->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", false); + } + + // 6. Return ? OrdinaryHasInstance(target, object). + return JSFunction::OrdinaryHasInstance(thread, target, object); + } + + // ecma6.0 6.2.4.4 + JSHandle JSObject::FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc) + { + // 1. If Desc is undefined, return undefined + if (desc.IsEmpty()) { + return JSHandle(thread, JSTaggedValue::Undefined()); + } + + // 2. Let obj be ObjectCreate(%ObjectPrototype%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc(env->GetObjectFunction()); + JSHandle objHandle = thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(objFunc); + + auto globalConst = thread->GlobalConstants(); + // 4. If Desc has a [[Value]] field, then Perform CreateDataProperty(obj, "value", Desc.[[Value]]). + if (desc.HasValue()) { + JSHandle valueStr = globalConst->GetHandledValueString(); + bool success = CreateDataProperty(thread, objHandle, valueStr, desc.GetValue()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 5. If Desc has a [[Writable]] field, then Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]). + if (desc.HasWritable()) { + JSHandle writableStr = globalConst->GetHandledWritableString(); + JSHandle writable(thread, JSTaggedValue(desc.IsWritable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, writableStr, writable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 6. If Desc has a [[Get]] field, then Perform CreateDataProperty(obj, "get", Desc.[[Get]]). + if (desc.HasGetter()) { + JSHandle getStr = globalConst->GetHandledGetString(); + bool success = CreateDataProperty(thread, objHandle, getStr, desc.GetGetter()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 7. If Desc has a [[Set]] field, then Perform CreateDataProperty(obj, "set", Desc.[[Set]]) + if (desc.HasSetter()) { + JSHandle setStr = globalConst->GetHandledSetString(); + bool success = CreateDataProperty(thread, objHandle, setStr, desc.GetSetter()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 8. If Desc has an [[Enumerable]] field, then Perform CreateDataProperty(obj, "enumerable", + // Desc.[[Enumerable]]). + if (desc.HasEnumerable()) { + JSHandle enumerableStr = globalConst->GetHandledEnumerableString(); + JSHandle enumerable(thread, JSTaggedValue(desc.IsEnumerable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, enumerableStr, enumerable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 9. If Desc has a [[Configurable]] field, then Perform CreateDataProperty(obj , "configurable", + // Desc.[[Configurable]]). + if (desc.HasConfigurable()) { + JSHandle configurableStr = globalConst->GetHandledConfigurableString(); + JSHandle configurable(thread, JSTaggedValue(desc.IsConfigurable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, configurableStr, configurable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + return JSHandle(objHandle); + } + + bool JSObject::ToPropertyDescriptorFast(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) + { + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (jsType != JSType::JS_OBJECT) { + return false; + } + if (hclass->IsDictionaryMode()) { + return false; + } + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + if (hclass->GetPrototype() != env->GetObjectFunctionPrototype().GetTaggedValue()) { + return false; + } + if (JSObject::Cast(hclass->GetPrototype().GetTaggedObject())->GetClass() != + env->GetObjectFunctionPrototypeClass().GetObject()) { + return false; + } + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + uint32_t propsNumber = hclass->NumberOfProps(); + for (uint32_t i = 0; i < propsNumber; i++) { + auto attr = layoutInfo->GetAttr(i); + if (attr.IsAccessor()) { + return false; + } + auto key = layoutInfo->GetKey(i); + auto value = JSObject::Cast(obj->GetTaggedObject())->GetProperty(hclass, attr); + if (key == globalConst->GetEnumerableString()) { + bool enumerable = value.ToBoolean(); + desc.SetEnumerable(enumerable); + } else if (key == globalConst->GetConfigurableString()) { + bool configurable = value.ToBoolean(); + desc.SetConfigurable(configurable); + } else if (key == globalConst->GetValueString()) { + auto handleValue = JSHandle(thread, value); + desc.SetValue(handleValue); + } else if (key == globalConst->GetWritableString()) { + bool writable = value.ToBoolean(); + desc.SetWritable(writable); + } else if (key == globalConst->GetGetString()) { + if (!value.IsCallable()) { + return false; + } + auto getter = JSHandle(thread, value); + desc.SetGetter(getter); + } else if (key == globalConst->GetSetString()) { + if (!value.IsCallable()) { + return false; + } + auto setter = JSHandle(thread, value); + desc.SetSetter(setter); + } + } + + if (desc.IsAccessorDescriptor()) { + // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. + if (desc.HasValue() || desc.HasWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "either Value or Writable is present", true); + } + } + return true; + } + + // ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj ) + void JSObject::ToPropertyDescriptor(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) + { + if (!obj->IsECMAObject()) { + // 2. If Type(Obj) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR(thread, "ToPropertyDescriptor error obj is not Object"); + } + + if (ToPropertyDescriptorFast(thread, obj, desc)) { + return; + } + auto globalConst = thread->GlobalConstants(); + // 3. Let desc be a new Property Descriptor that initially has no fields. + // 4. Let hasEnumerable be HasProperty(Obj, "enumerable") + { + ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetEnumerableString()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool enumerable = value->IsException() ? false : value->ToBoolean(); + desc.SetEnumerable(enumerable); + } + } + // 7. Let hasConfigurable be HasProperty(Obj, "configurable"). + { + ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetConfigurableString()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool conf = value->IsException() ? false : value->ToBoolean(); + desc.SetConfigurable(conf); + } + } + // 10. Let hasValue be HasProperty(Obj, "value"). + { + ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetValueString()); + if (op.IsFound()) { + JSHandle prop = op.FastGetValue(); + desc.SetValue(prop); + } + } + // 13. Let hasWritable be HasProperty(Obj, "writable"). + { + ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetWritableString()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool writable = value->IsException() ? false : value->ToBoolean(); + desc.SetWritable(writable); + } + } + // 16. Let hasGet be HasProperty(Obj, "get"). + { + ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetGetString()); + if (op.IsFound()) { + JSHandle getter = op.FastGetValue(); + if (!getter->IsCallable() && !getter->IsUndefined()) { + THROW_TYPE_ERROR(thread, "getter not callable or undefined"); + } + desc.SetGetter(getter); + } + } + + // 19. Let hasSet be HasProperty(Obj, "set"). + { + ObjectOperator op(thread, obj.GetTaggedValue(), globalConst->GetSetString()); + if (op.IsFound()) { + JSHandle setter = op.FastGetValue(); + if (!setter->IsCallable() && !setter->IsUndefined()) { + THROW_TYPE_ERROR(thread, "setter not callable or undefined"); + } + desc.SetSetter(setter); + } + } + + // 22. If either desc.[[Get]] or desc.[[Set]] is present, then + if (desc.IsAccessorDescriptor()) { + // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. + if (desc.HasValue() || desc.HasWritable()) { + THROW_TYPE_ERROR(thread, "either desc.[[Value]] or desc.[[Writable]] is present"); + } + } + // 23. Return desc. + } + + const CString JSObject::ExtractConstructorAndRecordName(JSThread *thread, TaggedObject *obj, bool noAllocate, + bool *isCallGetter) + { + CString result = ""; + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + JSHandle contructorKey = globalConst->GetHandledConstructorString(); + JSTaggedValue objConstructor = ObjectFastOperator::GetPropertyByName(thread, JSTaggedValue(obj), + contructorKey.GetTaggedValue(), noAllocate, + isCallGetter); + if (*isCallGetter) { + return "JSObject"; + } + + if (!objConstructor.IsJSFunction()) { + return "JSObject"; + } + + JSFunctionBase *func = JSFunctionBase::Cast(objConstructor.GetTaggedObject()); + Method *method = Method::Cast(func->GetMethod().GetTaggedObject()); + MethodLiteral *methodLiteral = method->GetMethodLiteral(); + if (methodLiteral == nullptr) { + return "JSObject"; + } + const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); + panda_file::File::EntityId methodId = methodLiteral->GetMethodId(); + const CString &nameStr = MethodLiteral::ParseFunctionNameToCString(jsPandaFile, methodId); + const CString &moduleStr = method->GetRecordNameStr(); + + if (!moduleStr.empty()) { + result.append(moduleStr).append(" "); + } + if (nameStr.empty()) { + result.append("JSObject"); + } else { + result.append(nameStr); + } + DebugInfoExtractor *debugExtractor = + JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); + if (debugExtractor == nullptr) { + return result; + } + int32_t line = debugExtractor->GetFristLine(methodId); + std::string fileName = debugExtractor->GetSourceFile(methodId); + return result.append("(").append(fileName).append(std::to_string(line)).append(")"); + } + + JSHandle JSObject::SpeciesConstructor(JSThread *thread, const JSHandle &obj, + const JSHandle &defaultConstructor) + { + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // Assert: Type(O) is Object. + ASSERT_PRINT(obj->IsECMAObject(), "obj must be js object"); + + // Let C be Get(O, "constructor"). + JSHandle contructorKey = globalConst->GetHandledConstructorString(); + JSHandle objConstructor(JSTaggedValue::GetProperty(thread, JSHandle(obj), + contructorKey).GetValue()); + // ReturnIfAbrupt(C). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + return SlowSpeciesConstructor(thread, objConstructor, defaultConstructor); + } + + JSHandle JSObject::SlowSpeciesConstructor(JSThread *thread, + const JSHandle &objConstructor, + const JSHandle &defaultConstructor) + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + if (objConstructor->IsUndefined()) { + return defaultConstructor; + } + // If Type(C) is not Object, throw a TypeError exception. + if (!objConstructor->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is not Object", + JSHandle(thread, JSTaggedValue::Exception())); + } + // Let S be Get(C, @@species). + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesConstructor = GetProperty(thread, objConstructor, speciesSymbol).GetValue(); + // ReturnIfAbrupt(S). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + // If S is either undefined or null, return defaultConstructor. + if (speciesConstructor->IsUndefined() || speciesConstructor->IsNull()) { + return defaultConstructor; + } + // If IsConstructor(S) is true, return S. + if (speciesConstructor->IsConstructor()) { + return speciesConstructor; + } + // Throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Constructor", + JSHandle(thread, JSTaggedValue::Exception())); + return JSHandle(thread, JSTaggedValue::Exception()); + } + + // 6.2.4.6 CompletePropertyDescriptor ( Desc ) + void PropertyDescriptor::CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc) + { + // 1. ReturnIfAbrupt(Desc). + // 2. Assert: Desc is a Property Descriptor + // 3. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined, + // [[Enumerable]]: false, [[Configurable]]: false}. + // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then + if (!desc.IsAccessorDescriptor()) { + // a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]]. + // b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]]. + if (!desc.HasValue()) { + desc.SetValue(JSHandle(thread, JSTaggedValue::Undefined())); + } + if (!desc.HasWritable()) { + desc.SetWritable(false); + } + } else { + // a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]]. + // b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]]. + // Default value of Get and Set is undefined. + } + // 6. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]]. + // 7. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]]. + if (!desc.HasEnumerable()) { + desc.SetEnumerable(false); + } + if (!desc.HasConfigurable()) { + desc.SetConfigurable(false); + } + } + + // static + // When receiver has no elements and there is no enum cache and elements on receiver's prototype chain, + // the enum cache is a simple enum cache. + // When receiver and receiver's prototype chain have no elements, and the prototype is not modified, + // the enum cache is a enum cache with protochain + bool JSObject::IsSimpleEnumCacheValid(JSTaggedValue receiver) + { + DISALLOW_GARBAGE_COLLECTION; + uint32_t numOfElements = JSObject::Cast(receiver.GetTaggedObject())->GetNumberOfElements(); + if (numOfElements > 0) { + return false; + } + + JSTaggedValue current = JSObject::GetPrototype(receiver); + while (current.IsHeapObject()) { + JSObject *currentObj = JSObject::Cast(current.GetTaggedObject()); + uint32_t numOfCurrentElements = currentObj->GetNumberOfElements(); + if (numOfCurrentElements > 0) { + return false; + } + JSHClass *hclass = currentObj->GetJSHClass(); + JSTaggedValue protoEnumCache = hclass->GetEnumCache(); + if (!protoEnumCache.IsUndefined()) { + return false; + } + current = JSObject::GetPrototype(current); + } + return true; + } + + bool JSObject::IsEnumCacheWithProtoChainInfoValid(JSTaggedValue receiver) + { + DISALLOW_GARBAGE_COLLECTION; + // check elements of receiver + uint32_t numOfElements = JSObject::Cast(receiver.GetTaggedObject())->GetNumberOfElements(); + if (numOfElements > 0) { + return false; + } + // check protochain keys + JSTaggedValue proto = JSObject::GetPrototype(receiver); + if (!proto.IsECMAObject()) { + return false; + } + JSTaggedValue protoChangeMarker = proto.GetTaggedObject()->GetClass()->GetProtoChangeMarker(); + if (!protoChangeMarker.IsProtoChangeMarker()) { + return false; + } + if (ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())->GetHasChanged()) { + return false; + } + // check protochain elements + JSTaggedValue current = proto; + while (current.IsHeapObject()) { + JSObject *currentObj = JSObject::Cast(current.GetTaggedObject()); + uint32_t numOfCurrentElements = currentObj->GetNumberOfElements(); + if (numOfCurrentElements > 0) { + return false; + } + current = JSObject::GetPrototype(current); + } + return true; + } + + JSTaggedValue JSObject::TryGetEnumCache(JSThread *thread, JSTaggedValue obj) + { + if (obj.IsSlowKeysObject() || obj.GetTaggedObject()->GetClass()->IsDictionaryMode()) { + return JSTaggedValue::Undefined(); + } + JSTaggedValue enumCache = obj.GetTaggedObject()->GetClass()->GetEnumCache(); + EnumCacheKind kind = JSObject::GetEnumCacheKind(thread, enumCache); + bool isEnumCacheValid = false; + switch (kind) { + case EnumCacheKind::SIMPLE: + isEnumCacheValid = IsSimpleEnumCacheValid(obj); + break; + case EnumCacheKind::PROTOCHAIN: + isEnumCacheValid = IsEnumCacheWithProtoChainInfoValid(obj); + break; + default: + break; + } + if (!isEnumCacheValid) { + return JSTaggedValue::Undefined(); + } + return enumCache; + } + + // 13.7.5.15 EnumerateObjectProperties ( O ) + JSHandle JSObject::EnumerateObjectProperties(JSThread *thread, const JSHandle &obj) + { + JSHandle object; + if (obj->IsString()) { + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + object = JSHandle::Cast(JSPrimitiveRef::StringCreate(thread, obj, undefined)); + } else { + object = JSTaggedValue::ToPrototypeOrObj(thread, obj); + } + + JSMutableHandle keys(thread, JSTaggedValue::Undefined()); + JSMutableHandle cachedHclass(thread, JSTaggedValue::Undefined()); + if (object->IsNull() || object->IsUndefined() || object->IsJSNativePointer()) { + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + keys.Update(factory->EmptyArray()); + return factory->NewJSForinIterator(undefined, keys, cachedHclass); + } + keys.Update(TryGetEnumCache(thread, object.GetTaggedValue())); + if (!keys->IsUndefined()) { + cachedHclass.Update(JSTaggedValue(JSHandle::Cast(object)->GetJSHClass())); + return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object, keys, cachedHclass); + } + return LoadEnumerateProperties(thread, object); + } + + JSHandle JSObject::LoadEnumerateProperties(JSThread *thread, const JSHandle &object) + { + PropertyAccessor accessor(thread, object); + JSHandle fastKeys = accessor.GetKeysFast(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSForInIterator, thread); + JSMutableHandle keys(thread, JSTaggedValue::Undefined()); + JSMutableHandle cachedHclass(thread, JSTaggedValue::Undefined()); + if (fastKeys->IsUndefined()) { + keys.Update(accessor.GetKeysSlow()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSForInIterator, thread); + } else { + keys.Update(fastKeys); + cachedHclass.Update(accessor.GetCachedHclass()); + } + return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object, keys, cachedHclass); + } + + void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, + bool useForClass) + { + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + PropertyAttributes attr = useForClass ? PropertyAttributes::Default(true, false, true) + : PropertyAttributes::Default(); + + if (value->IsAccessorData()) { + attr.SetIsAccessor(true); + } + + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + AddElementInternal(thread, obj, index, value, attr); + return; + } + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + + void JSObject::DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(op.IsFound()); + op.DefineSetter(value); + } + + void JSObject::DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(op.IsFound()); + op.DefineGetter(value); + } + + JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle &properties, + JSTaggedValue ihcVal) + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + size_t length = properties->GetLength(); + uint32_t propsLen = 0; + for (size_t i = 0; i < length; i += 2) { // 2: skip a pair of key and value + if (properties->Get(i).IsHole()) { + break; + } + propsLen++; + } + if (propsLen <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY) { + JSHandle hclass; + bool isLiteral = false; + if (ihcVal.IsJSHClass()) { + hclass = JSHandle(thread, ihcVal); + } else { + hclass = factory->GetObjectLiteralHClass(properties, propsLen); + isLiteral = true; + } + if (hclass->IsTS()) { + if (CheckPropertiesForRep(properties, propsLen, hclass)) { + return CreateObjectFromPropertiesByIHClass(thread, properties, propsLen, hclass); + } else if (!isLiteral) { + hclass = factory->GetObjectLiteralHClass(properties, propsLen); + // if check failed, get literal object again + return CreateObjectFromPropertiesByIHClass(thread, properties, propsLen, hclass); + } + } + return CreateObjectFromProperties(thread, hclass, properties, propsLen); + } else { + JSHandle obj = factory->NewEmptyJSObject(0); // 0: no inline field + ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); + JSHClass::TransitionToDictionary(thread, obj); + JSObject::TryMigrateToGenericKindForJSObject(thread, obj, oldKind); + + JSMutableHandle dict( + thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propsLen))); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + for (size_t i = 0; i < propsLen; i++) { + PropertyAttributes attr = PropertyAttributes::Default(); + // 2: literal contains a pair of key-value + valueHandle.Update(properties->Get(i * 2 + 1)); + // 2: literal contains a pair of key-value + keyHandle.Update(properties->Get(i * 2)); + JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); + dict.Update(newDict); + } + obj->SetProperties(thread, dict); + return obj; + } + } + + JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, + const JSHandle &hclass, + const JSHandle &properties, + uint32_t propsLen) + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewOldSpaceObjLiteralByHClass(hclass); + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + + if (thread->IsPGOProfilerEnable()) { + // PGO need to track TrackType + JSHClass *oldHC = obj->GetJSHClass(); + LayoutInfo *layoutInfo = LayoutInfo::Cast(oldHC->GetLayout().GetTaggedObject()); + for (size_t i = 0; i < propsLen; i++) { + auto value = properties->Get(i * 2 + 1); + auto attr = layoutInfo->GetAttr(i); + if (attr.UpdateTrackType(value) && !oldHC->IsJSShared()) { + layoutInfo->SetNormalAttr(thread, i, attr); + } + obj->SetPropertyInlinedProps(thread, i, value); + } + } else { + for (size_t i = 0; i < propsLen; i++) { + // 2: literal contains a pair of key-value + obj->SetPropertyInlinedProps(thread, i, properties->Get(i * 2 + 1)); + } + } + return obj; + } + + JSHandle JSObject::CreateObjectFromPropertiesByIHClass(const JSThread *thread, + const JSHandle &properties, + uint32_t propsLen, + const JSHandle &ihc) + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj = factory->NewOldSpaceObjLiteralByHClass(ihc); + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + auto layout = LayoutInfo::Cast(ihc->GetLayout().GetTaggedObject()); + for (size_t i = 0; i < propsLen; i++) { + auto attr = layout->GetAttr(i); + auto value = JSObject::ConvertValueWithRep(attr, properties->Get(i * 2 + 1)); + ASSERT(value.first); + obj->SetPropertyInlinedPropsWithRep(thread, i, value.second); + } + return obj; + } + + bool JSObject::CheckPropertiesForRep( + const JSHandle &properties, uint32_t propsLen, const JSHandle &ihc) + { + auto layout = LayoutInfo::Cast(ihc->GetLayout().GetTaggedObject()); + for (size_t i = 0; i < propsLen; i++) { + auto attr = layout->GetAttr(i); + auto value = JSObject::ConvertValueWithRep(attr, properties->Get(i * 2 + 1)); + // If value.first is false, indicating that value cannot be converted to the expected value of + // representation. For example, the representation is INT, but the value type is string. + if (!value.first) { + return false; + } + } + return true; + } + + void JSObject::AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, PropertyAttributes attr) + { + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ASSERT_PRINT(attr.IsAccessor(), "Attr is not AccessorData"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(!op.IsFound()); + op.AddProperty(JSHandle::Cast(obj), JSHandle(value), attr); + } + + bool JSObject::UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value) + { + [[maybe_unused]] DisallowGarbageCollection noGc; + NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject()); + int entry = dict->FindEntry(key); + if (entry == -1) { + return false; + } + dict->UpdateValue(thread, entry, value); + return true; + } + + void JSObject::TrimInlinePropsSpace(const JSThread *thread, const JSHandle &object, + uint32_t numberInlinedProps) + { + if (numberInlinedProps > 0) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t newSize = object->GetClass()->GetObjectSize(); + size_t trimBytes = numberInlinedProps * JSTaggedValue::TaggedTypeSize(); + factory->FillFreeObject(ToUintPtr(*object) + newSize, trimBytes, RemoveSlots::YES, ToUintPtr(*object)); + } + } + + // The hash field may be a hash value, FunctionExtraInfo(JSNativePointer) or TaggedArray + void ECMAObject::SetHash(const JSThread *thread, int32_t hash, const JSHandle &obj) + { + JSTaggedType hashField = Barriers::GetValue(*obj, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsHeapObject()) { + // Hash position reserve in advance. + if (value.IsTaggedArray()) { + TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject()); + array->Set(thread, array->GetExtraLength() + HASH_INDEX, JSTaggedValue(hash)); + } else if (value.IsNativePointer()) { // FunctionExtraInfo + JSHandle nativePointer(thread, value); + JSHandle newArray = + thread->GetEcmaVM()->GetFactory()->NewTaggedArray(RESOLVED_MAX_SIZE); + newArray->SetExtraLength(0); + newArray->Set(thread, HASH_INDEX, JSTaggedValue(hash)); + newArray->Set(thread, FUNCTION_EXTRA_INDEX, nativePointer.GetTaggedValue()); + Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); + } else { + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + } else { + Barriers::SetPrimitive(*obj, HASH_OFFSET, JSTaggedValue(hash).GetRawData()); + } + } + + int32_t ECMAObject::GetHash() const + { + JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsHeapObject()) { + if (value.IsTaggedArray()) { + TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject()); + return array->Get(array->GetExtraLength() + HASH_INDEX).GetInt(); + } else { + // Default is 0 + return 0; + } + } + return value.GetInt(); + } + + bool ECMAObject::HasHash() const + { + JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsInt() && value.GetInt() == 0) { + return false; + } + return true; + } + + void *ECMAObject::GetNativePointerField(int32_t index) const + { + JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsTaggedArray()) { + auto array = TaggedArray::Cast(value); + if (static_cast(array->GetExtraLength()) > index) { + auto jsValue = array->Get(index); + if (UNLIKELY(!jsValue.IsJSNativePointer())) { + LOG_FULL(ERROR) << "jsValue is not js native pointer"; + return nullptr; + } + auto pointer = JSNativePointer::Cast(jsValue.GetTaggedObject()); + return pointer->GetExternalPointer(); + } + } + return nullptr; + } + + void ECMAObject::SetNativePointerField(const JSThread *thread, int32_t index, void *nativePointer, + const NativePointerCallback &callBack, void *data, size_t nativeBindingsize, Concurrent isConcurrent) + { + JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsTaggedArray()) { + JSHandle array(thread, value); + if (static_cast(array->GetExtraLength()) > index) { + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle current = JSHandle(thread, array->Get(thread, index)); + JSHandle obj(thread, this); + if (!current->IsHole() && nativePointer == nullptr) { + // Try to remove native pointer if exists. + vm->RemoveFromNativePointerList(*JSHandle(current)); + array->Set(thread, index, JSTaggedValue::Hole()); + } else if (obj->IsJSShared()) { + JSHandle pointer = + vm->GetFactory()->NewSJSNativePointer(nativePointer, callBack, data, false, nativeBindingsize); + array->Set(thread, index, pointer.GetTaggedValue()); + } else { + JSHandle pointer = vm->GetFactory()->NewJSNativePointer( + nativePointer, callBack, data, false, nativeBindingsize, isConcurrent); + array->Set(thread, index, pointer.GetTaggedValue()); + } + } + } + } + + int32_t ECMAObject::GetNativePointerFieldCount() const + { + int32_t len = 0; + JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsTaggedArray()) { + TaggedArray *array = TaggedArray::Cast(value.GetTaggedObject()); + len = static_cast(array->GetExtraLength()); + } + return len; + } + + void ECMAObject::SetNativePointerFieldCount(const JSThread *thread, int32_t count) + { + if (count == 0) { + return; + } + JSTaggedType hashField = Barriers::GetValue(this, HASH_OFFSET); + JSHandle value(thread, JSTaggedValue(hashField)); + JSHandle obj(thread, this); + JSHandle object(obj); + bool isShared = object->IsJSShared(); + if (value->IsHeapObject()) { + if (value->IsTaggedArray()) { + JSHandle array(value); + // Native Pointer field count is fixed. + if (array->GetExtraLength() == 0) { + JSHandle newArray = + isShared ? thread->GetEcmaVM()->GetFactory()->NewSTaggedArray(count + RESOLVED_MAX_SIZE) + : thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE); + newArray->SetExtraLength(count); + newArray->Set(thread, count + HASH_INDEX, array->Get(HASH_INDEX)); + newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, array->Get(FUNCTION_EXTRA_INDEX)); + Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); + } + } else if (value->IsJSNativePointer()) { + JSHandle newArray = + isShared ? thread->GetEcmaVM()->GetFactory()->NewSTaggedArray(count + RESOLVED_MAX_SIZE) + : thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + RESOLVED_MAX_SIZE); + newArray->SetExtraLength(count); + newArray->Set(thread, count + HASH_INDEX, JSTaggedValue(0)); + newArray->Set(thread, count + FUNCTION_EXTRA_INDEX, value); + Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); + } else { + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + } else { + JSHandle newArray = isShared ? thread->GetEcmaVM()->GetFactory()->NewSTaggedArray(count + 1) + : thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + 1); + newArray->SetExtraLength(count); + newArray->Set(thread, count + HASH_INDEX, value); + Barriers::SetObject(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); + } + } + + bool JSObject::ElementsAndPropertiesIsEmpty() const + { + if (TaggedArray::Cast(GetElements().GetTaggedObject())->GetLength() == 0 && + TaggedArray::Cast(GetProperties().GetTaggedObject())->GetLength() == 0) { + return true; + } + return false; + } + + void JSObject::TryMigrateToGenericKindForJSObject(const JSThread *thread, const JSHandle &obj, + const ElementsKind oldKind) + { + if (obj->IsJSArray() && HasMutantTaggedArrayElements(obj)) { + Elements::MigrateArrayWithKind(thread, obj, oldKind, ElementsKind::GENERIC); + } + } + } // namespace panda::ecmascript + \ No newline at end of file diff --git a/ecmascript/js_proxy.h b/ecmascript/js_proxy.h index 06df8b8c06..8244ab1925 100644 --- a/ecmascript/js_proxy.h +++ b/ecmascript/js_proxy.h @@ -28,14 +28,14 @@ public: // ES6 9.5.15 ProxyCreate(target, handler) static JSHandle ProxyCreate(JSThread *thread, const JSHandle &target, const JSHandle &handler); - // ES6 9.5.1 [[GetPrototypeOf]] ( ) - static JSTaggedValue GetPrototype(JSThread *thread, const JSHandle &proxy); - // ES6 9.5.2 [[SetPrototypeOf]] (V) - static bool SetPrototype(JSThread *thread, const JSHandle &proxy, const JSHandle &proto); // ES6 9.5.3 [[IsExtensible]] ( ) static bool IsExtensible(JSThread *thread, const JSHandle &proxy); // ES6 9.5.4 [[PreventExtensions]] ( ) static bool PreventExtensions(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.1 [[GetPrototypeOf]] ( ) + static JSTaggedValue GetPrototype(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.2 [[SetPrototypeOf]] (V) + static bool SetPrototype(JSThread *thread, const JSHandle &proxy, const JSHandle &proto); // ES6 9.5.5 [[GetOwnProperty]] (P) static bool GetOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, PropertyDescriptor &desc); -- Gitee