/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/object_operator.h" #include "ecmascript/ecma_string.h" #include "ecmascript/global_env.h" #include "ecmascript/global_dictionary-inl.h" #include "ecmascript/js_array.h" #include "ecmascript/property_attributes.h" #include "ecmascript/tagged_array.h" #include "ecmascript/tagged_dictionary.h" #include "ecmascript/tests/test_helper.h" using namespace panda::ecmascript; namespace panda::test { class ObjectOperatorTest : public BaseTestWithScope { }; HWTEST_F_L0(ObjectOperatorTest, SetAttr) { JSHandle handleValue1(thread, JSTaggedValue(1)); JSHandle handleValue2(thread, JSTaggedValue(2)); ObjectOperator objectOperator1(thread, handleValue1); ObjectOperator objectOperator2(thread, handleValue2); uint32_t handleAttr1 = 2; objectOperator1.SetAttr(handleAttr1); EXPECT_EQ(objectOperator1.GetAttr().GetPropertyMetaData(), 2); EXPECT_FALSE(objectOperator1.IsPrimitiveAttr()); PropertyAttributes handleAttr2(JSTaggedValue(0)); objectOperator2.SetAttr(handleAttr2); EXPECT_TRUE(objectOperator2.IsPrimitiveAttr()); } HWTEST_F_L0(ObjectOperatorTest, SetFastMode) { JSHandle handleValue(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleValue); EXPECT_FALSE(objectOperator.IsFastMode()); objectOperator.SetFastMode(false); EXPECT_FALSE(objectOperator.IsFastMode()); objectOperator.SetFastMode(true); EXPECT_TRUE(objectOperator.IsFastMode()); } HWTEST_F_L0(ObjectOperatorTest, SetIsOnPrototype) { JSHandle handleValue(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleValue); EXPECT_TRUE(objectOperator.IsOnPrototype()); objectOperator.SetIsOnPrototype(true); EXPECT_TRUE(objectOperator.IsOnPrototype()); objectOperator.SetIsOnPrototype(false); EXPECT_FALSE(objectOperator.IsOnPrototype()); } HWTEST_F_L0(ObjectOperatorTest, SetHasReceiver) { JSHandle handleValue(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleValue); EXPECT_FALSE(objectOperator.HasReceiver()); objectOperator.SetHasReceiver(true); EXPECT_TRUE(objectOperator.HasReceiver()); objectOperator.SetHasReceiver(false); EXPECT_FALSE(objectOperator.HasReceiver()); } HWTEST_F_L0(ObjectOperatorTest, SetIsTransition) { JSHandle handleValue(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleValue); EXPECT_FALSE(objectOperator.IsTransition()); objectOperator.SetIsTransition(true); EXPECT_TRUE(objectOperator.IsTransition()); objectOperator.SetIsTransition(false); EXPECT_FALSE(objectOperator.IsTransition()); } HWTEST_F_L0(ObjectOperatorTest, SetIndex) { JSHandle handleValue(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleValue); uint32_t index = 1; objectOperator.SetIndex(index); EXPECT_EQ(objectOperator.GetIndex(), 1U); EXPECT_TRUE(objectOperator.IsFound()); objectOperator.SetIndex(ObjectOperator::NOT_FOUND_INDEX); EXPECT_FALSE(objectOperator.IsFound()); } HWTEST_F_L0(ObjectOperatorTest, SetValue) { JSHandle handleKey(thread, JSTaggedValue(1)); JSHandle handleValue1(thread, JSTaggedValue(2)); JSHandle handleValue2(thread, JSTaggedValue(3)); ObjectOperator objectOperator(thread, handleKey); EXPECT_TRUE(objectOperator.GetValue().IsUndefined()); objectOperator.SetValue(handleValue1.GetTaggedValue()); EXPECT_EQ(objectOperator.GetValue().GetInt(), 2); objectOperator.SetValue(handleValue2.GetTaggedValue()); EXPECT_EQ(objectOperator.GetValue().GetInt(), 3); } HWTEST_F_L0(ObjectOperatorTest, SetAsDefaultAttr) { JSHandle handleKey(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleKey); objectOperator.SetAsDefaultAttr(); EXPECT_EQ(objectOperator.GetIndex(), ObjectOperator::NOT_FOUND_INDEX); EXPECT_TRUE(objectOperator.GetValue().IsUndefined()); EXPECT_FALSE(objectOperator.IsFastMode()); EXPECT_FALSE(objectOperator.IsTransition()); EXPECT_EQ(objectOperator.GetAttr().GetPropertyMetaData(), 7); } HWTEST_F_L0(ObjectOperatorTest, GetHolder) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleHolder = factory->NewEmptyJSObject(); JSHandle handleKey(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleHolder, handleKey); EXPECT_TRUE(objectOperator.HasHolder()); EXPECT_TRUE(objectOperator.GetHolder().GetTaggedValue().IsECMAObject()); } HWTEST_F_L0(ObjectOperatorTest, GetReceiver) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleNameString = factory->NewFromASCII("name"); JSHandle handleReceiver(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleReceiver.GetTaggedValue(), handleNameString.GetTaggedValue()); EXPECT_EQ(objectOperator.GetReceiver()->GetInt(), 1); } HWTEST_F_L0(ObjectOperatorTest, GetElementIndex) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleKey1(thread, JSTaggedValue(1)); JSHandle handleKey2(thread, JSTaggedValue(2.0)); JSHandle handleKey3(factory->NewFromASCII("2")); ObjectOperator objectOperator1(thread, handleKey1); ObjectOperator objectOperator2(thread, handleKey2); ObjectOperator objectOperator3(thread, handleKey3); EXPECT_EQ(objectOperator1.GetElementIndex(), 1U); EXPECT_EQ(objectOperator2.GetElementIndex(), 2U); EXPECT_EQ(objectOperator3.GetElementIndex(), 2U); } HWTEST_F_L0(ObjectOperatorTest, GetKey) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleKey1(thread, JSTaggedValue(1)); JSHandle handleKey2(factory->NewFromASCII("name")); ObjectOperator objectOperator1(thread, handleKey1); ObjectOperator objectOperator2(thread, handleKey2); EXPECT_TRUE(objectOperator1.GetKey()->IsUndefined()); EXPECT_TRUE(objectOperator1.IsElement()); EXPECT_FALSE(objectOperator2.GetKey()->IsUndefined()); EXPECT_FALSE(objectOperator2.IsElement()); } HWTEST_F_L0(ObjectOperatorTest, GetThread) { JSHandle handleKey(thread, JSTaggedValue(1)); ObjectOperator objectOperator(thread, handleKey); EXPECT_TRUE(objectOperator.GetThread() != nullptr); } JSTaggedValue TestDefinedGetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) { // 12 : test case return JSTaggedValue(12); } JSTaggedValue TestDefinedSetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) { // 12 : test case return JSTaggedValue(12); } static JSFunction *JSObjectTestCreate(JSThread *thread) { JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); return globalEnv->GetObjectFunction().GetObject(); } HWTEST_F_L0(ObjectOperatorTest, ToPropertyDescriptor) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleKey(factory->NewFromASCII("property")); JSHandle handleValue(thread, JSTaggedValue(1)); JSHandle handleAccessor = factory->NewAccessorData(); JSHandle handleInternalAccessor = factory->NewInternalAccessor(nullptr, reinterpret_cast(TestDefinedGetter)); JSHandle handleHolder(factory->NewEmptyJSObject()); PropertyDescriptor handleDesc(thread); handleDesc.SetGetter(handleValue); handleDesc.SetSetter(handleValue); PropertyDescriptor handleDesc1(thread); handleDesc1.SetWritable(true); PropertyDescriptor handleDesc2(thread); handleDesc2.SetConfigurable(true); handleDesc2.SetEnumerable(true); PropertyDescriptor handleDesc3(thread); PropertyAttributes handleAttr(handleDesc); ObjectOperator objectOperator(thread, handleHolder, handleKey); objectOperator.SetIndex(1); // object is not IsAccessorDescriptor objectOperator.ToPropertyDescriptor(handleDesc1); EXPECT_TRUE(!handleDesc1.IsWritable()); EXPECT_TRUE(handleDesc1.GetValue()->IsUndefined()); // object is IsAccessorDescriptor objectOperator.SetAttr(handleAttr); objectOperator.SetValue(handleAccessor.GetTaggedValue()); objectOperator.ToPropertyDescriptor(handleDesc2); EXPECT_FALSE(handleDesc2.IsEnumerable()); EXPECT_FALSE(handleDesc2.IsConfigurable()); EXPECT_TRUE(handleDesc2.GetGetter()->IsUndefined()); EXPECT_TRUE(handleDesc2.GetSetter()->IsUndefined()); objectOperator.SetValue(handleInternalAccessor.GetTaggedValue()); objectOperator.ToPropertyDescriptor(handleDesc3); EXPECT_EQ(handleDesc3.GetValue()->GetInt(), 12); } HWTEST_F_L0(ObjectOperatorTest, handleKey) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleString(factory->NewFromASCII("key")); JSHandle handleObject(factory->NewEmptyJSObject()); JSHandle handleSymbol(factory->NewJSSymbol()); JSHandle handleKey1(thread, JSTaggedValue(-1)); JSHandle handleKey2(thread, JSTaggedValue(-1.11)); JSHandle handleKey3(thread, handleString.GetTaggedValue()); JSHandle handleKey4(thread, handleSymbol.GetTaggedValue()); JSHandle handleKey5(thread, handleObject.GetTaggedValue()); JSHandle handleKey6(thread, JSTaggedValue(1.11)); ObjectOperator objectOperator1(thread, handleKey1); ObjectOperator objectOperator2(thread, handleKey2); ObjectOperator objectOperator3(thread, handleKey3); ObjectOperator objectOperator4(thread, handleKey4); ObjectOperator objectOperator5(thread, handleKey5); ObjectOperator objectOperator6(thread, handleKey6); JSHandle handleEcmaStrTo1(objectOperator1.GetKey()); EXPECT_STREQ("-1", EcmaStringAccessor(handleEcmaStrTo1).ToCString().c_str()); JSHandle handleEcmaStrTo2(objectOperator2.GetKey()); EXPECT_STREQ("-1.11", EcmaStringAccessor(handleEcmaStrTo2).ToCString().c_str()); EcmaString *str1 = EcmaString::Cast(objectOperator3.GetKey()->GetTaggedObject()); EXPECT_TRUE(EcmaStringAccessor(str1).IsInternString()); EXPECT_TRUE(objectOperator4.GetKey()->IsSymbol()); EcmaString *str2 = EcmaString::Cast(objectOperator5.GetKey()->GetTaggedObject()); EXPECT_TRUE(EcmaStringAccessor(str2).IsInternString()); JSHandle handleEcmaStrTo3(objectOperator6.GetKey()); EXPECT_STREQ("1.11", EcmaStringAccessor(handleEcmaStrTo3).ToCString().c_str()); } HWTEST_F_L0(ObjectOperatorTest, FastGetValue) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle handleNameString = factory->NewFromASCII("name"); JSHandle handleAccessorData = factory->NewAccessorData(); JSHandle handleReceiver(thread, JSTaggedValue(123)); JSHandle handleValue1(factory->NewPropertyBox(handleReceiver)); JSHandle handleValue2(handleAccessorData); JSHandle handleValue3(thread, JSTaggedValue(1)); ObjectOperator objectOperator1(thread, handleValue3); objectOperator1.SetIndex(1); objectOperator1.SetValue(handleValue1.GetTaggedValue()); EXPECT_EQ(objectOperator1.FastGetValue()->GetInt(), 123); // op for fast path ObjectOperator objectOperator2(thread, handleReceiver.GetTaggedValue(), handleNameString.GetTaggedValue()); objectOperator2.SetIndex(1); objectOperator2.SetValue(handleValue2.GetTaggedValue()); PropertyDescriptor handleDesc(thread); handleDesc.SetGetter(handleValue2); handleDesc.SetSetter(handleValue2); objectOperator2.SetAttr(PropertyAttributes(handleDesc)); EXPECT_TRUE(objectOperator2.FastGetValue()->IsUndefined()); JSHandle handleGetter = factory->NewJSFunction(env, reinterpret_cast(TestDefinedGetter)); handleAccessorData->SetGetter(thread, handleGetter.GetTaggedValue()); EXPECT_EQ(objectOperator2.FastGetValue()->GetInt(), 12); } HWTEST_F_L0(ObjectOperatorTest, ReLookupPropertyInReceiver_001) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle handleKey(thread, JSTaggedValue(2)); JSHandle handleName(factory->NewFromASCII("123")); JSHandle handleHolder(factory->NewFromASCII("12")); JSHandle handleReceiver(factory->NewJSString(handleName, undefined)); // Receiver is string ObjectOperator objectOperator1(thread, handleHolder, handleReceiver, handleKey); objectOperator1.ReLookupPropertyInReceiver(); EXPECT_EQ(objectOperator1.GetIndex(), 2U); EXPECT_TRUE(objectOperator1.GetValue().IsString()); EXPECT_EQ(objectOperator1.GetAttr().GetPropertyMetaData(), 2); EXPECT_TRUE(objectOperator1.IsFastMode()); // Receiver is not DictionaryMode JSHandle handleObject = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); for (int i = 0; i < 3; i++) { JSHandle newKey(thread, JSTaggedValue(i)); JSObject::SetProperty(thread, JSHandle(handleObject), newKey, newKey); } ObjectOperator objectOperator2(thread, handleHolder, JSHandle(handleObject), handleKey); objectOperator2.ReLookupPropertyInReceiver(); EXPECT_EQ(objectOperator2.GetIndex(), 2U); EXPECT_EQ(objectOperator2.GetValue().GetInt(), 2); EXPECT_EQ(objectOperator2.GetAttr().GetPropertyMetaData(), 7); EXPECT_TRUE(objectOperator2.IsFastMode()); // Receiver is DictionaryMode JSObject::DeleteProperty(thread, (handleObject), handleKey); ObjectOperator objectOperator3(thread, handleHolder, JSHandle(handleObject), handleKey); objectOperator3.ReLookupPropertyInReceiver(); // no key find EXPECT_EQ(objectOperator3.GetIndex(), ObjectOperator::NOT_FOUND_INDEX); EXPECT_TRUE(objectOperator3.GetValue().IsUndefined()); EXPECT_EQ(objectOperator3.GetAttr().GetPropertyMetaData(), 0); EXPECT_FALSE(objectOperator3.IsFastMode()); } HWTEST_F_L0(ObjectOperatorTest, ReLookupPropertyInReceiver_002) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle handleKey(factory->NewFromASCII("key")); JSHandle handleKey1(thread, JSTaggedValue(0)); JSHandle handleKey2(thread, JSTaggedValue(1)); JSHandle handleHolder(factory->NewFromASCII("12")); JSHandle globalObj = env->GetJSGlobalObject(); JSHandle handleReceiver(globalObj); PropertyAttributes handleAttr(4); // Receiver is JSGlobalObject(no properties) ObjectOperator objectOperator1(thread, handleHolder, JSHandle(handleReceiver), handleKey); objectOperator1.ReLookupPropertyInReceiver(); EXPECT_EQ(objectOperator1.GetIndex(), ObjectOperator::NOT_FOUND_INDEX); EXPECT_TRUE(objectOperator1.GetValue().IsUndefined()); EXPECT_EQ(objectOperator1.GetAttr().GetPropertyMetaData(), 0); EXPECT_FALSE(objectOperator1.IsFastMode()); // Receiver is JSGlobalObject(properties) JSMutableHandle receiverDict(thread, handleReceiver->GetProperties()); JSHandle handleDict = GlobalDictionary::Create(thread, 4); // numberofElements = 4 receiverDict.Update(handleDict.GetTaggedValue()); JSHandle cellHandle = factory->NewPropertyBox(handleKey); cellHandle->SetValue(thread, JSTaggedValue(4)); JSHandle handleProperties = GlobalDictionary::PutIfAbsent(thread, receiverDict, handleKey, JSHandle(cellHandle), handleAttr); handleReceiver->SetProperties(thread, handleProperties.GetTaggedValue()); int keyEntry = handleProperties->FindEntry(handleKey.GetTaggedValue()); ObjectOperator objectOperator2(thread, handleHolder, JSHandle(handleReceiver), handleKey); objectOperator2.ReLookupPropertyInReceiver(); EXPECT_EQ(objectOperator2.GetIndex(), static_cast(keyEntry)); EXPECT_TRUE(objectOperator2.GetValue().IsPropertyBox()); EXPECT_EQ(objectOperator2.GetAttr().GetPropertyMetaData(), handleAttr.GetPropertyMetaData()); EXPECT_TRUE(objectOperator2.IsFastMode()); } HWTEST_F_L0(ObjectOperatorTest, ReLookupPropertyInReceiver_003) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle handleHolder(factory->NewFromASCII("12")); JSHandle handleKey(factory->NewFromASCII("key")); JSHandle handleKey1(thread, JSTaggedValue(1)); JSHandle handleObject = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSObject::SetProperty(thread, JSHandle(handleObject), handleKey, handleKey1); JSObject::SetProperty(thread, JSHandle(handleObject), handleKey1, handleKey1); // Receiver is not DictionaryMode ObjectOperator objectOperator1(thread, handleHolder, JSHandle(handleObject), handleKey); objectOperator1.ReLookupPropertyInReceiver(); EXPECT_EQ(objectOperator1.GetIndex(), 0U); EXPECT_EQ(objectOperator1.GetValue().GetInt(), 1); EXPECT_EQ(objectOperator1.GetAttr().GetPropertyMetaData(), 7); // default attribute EXPECT_TRUE(objectOperator1.IsFastMode()); // Receiver is DictionaryMode JSObject::DeleteProperty(thread, (handleObject), handleKey); ObjectOperator objectOperator2(thread, handleHolder, JSHandle(handleObject), handleKey); objectOperator2.ReLookupPropertyInReceiver(); EXPECT_EQ(objectOperator2.GetIndex(), ObjectOperator::NOT_FOUND_INDEX); EXPECT_TRUE(objectOperator2.GetValue().IsUndefined()); EXPECT_EQ(objectOperator2.GetAttr().GetPropertyMetaData(), 0); // default attribute EXPECT_FALSE(objectOperator2.IsFastMode()); } HWTEST_F_L0(ObjectOperatorTest, LookupProperty_001) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleKey(thread, JSTaggedValue(1)); JSHandle handleValue(thread, JSTaggedValue(2)); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle handleObject = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSObject::SetProperty(thread, JSHandle(handleObject), handleKey, handleValue); JSHandle handleObject1 = JSObject::ObjectCreate(thread, handleObject); ObjectOperator objectOperator(thread, handleObject1, handleKey); // lookup for key is element objectOperator.LookupProperty(); EXPECT_TRUE(objectOperator.IsOnPrototype()); EXPECT_EQ(objectOperator.GetIndex(), 1U); EXPECT_EQ(objectOperator.GetAttr().GetPropertyMetaData(), 7); EXPECT_EQ(objectOperator.GetValue().GetInt(), 2); EXPECT_TRUE(objectOperator.IsFastMode()); } HWTEST_F_L0(ObjectOperatorTest, LookupProperty_002) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handleKey1(thread, JSTaggedValue(1)); JSHandle handleValue1(thread, JSTaggedValue(1)); JSHandle handleKey2(factory->NewFromASCII("key2")); JSHandle handleValue2(thread, JSTaggedValue(2)); JSHandle objFunc(thread, JSObjectTestCreate(thread)); JSHandle handleObject = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); JSObject::SetProperty(thread, JSHandle(handleObject), handleKey1, handleValue1); JSObject::SetProperty(thread, JSHandle(handleObject), handleKey2, handleValue2); JSHandle handleObject1 = JSObject::ObjectCreate(thread, handleObject); ObjectOperator objectOperator(thread, handleObject1, handleKey2); // lookup for key is not element objectOperator.LookupProperty(); EXPECT_TRUE(objectOperator.IsOnPrototype()); EXPECT_EQ(objectOperator.GetIndex(), 0U); EXPECT_EQ(objectOperator.GetAttr().GetPropertyMetaData(), 7); EXPECT_EQ(objectOperator.GetValue().GetInt(), 2); EXPECT_TRUE(objectOperator.IsFastMode()); } HWTEST_F_L0(ObjectOperatorTest, GlobalLookupProperty) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle handleKey(thread, JSTaggedValue(1)); JSHandle handleValue(thread, JSTaggedValue(2)); JSHandle globalObj = env->GetJSGlobalObject(); JSHandle handleGlobalObject(globalObj); JSObject::SetProperty(thread, JSHandle(handleGlobalObject), handleKey, handleValue); JSHandle handleObject = JSObject::ObjectCreate(thread, handleGlobalObject); JSHandle handleGlobalObj(JSHandle::Cast(handleObject)); ObjectOperator objectOperator(thread, handleGlobalObj, handleKey); objectOperator.GlobalLookupProperty(); EXPECT_TRUE(objectOperator.IsOnPrototype()); EXPECT_EQ(objectOperator.GetIndex(), 1U); EXPECT_EQ(objectOperator.GetAttr().GetPropertyMetaData(), 7); EXPECT_EQ(objectOperator.GetValue().GetInt(), 2); EXPECT_TRUE(objectOperator.IsFastMode()); } } // namespace panda::test