From df7e3f4d2bf725b8d4268cb9755843f051fbb91a Mon Sep 17 00:00:00 2001 From: liu-zelin Date: Tue, 9 Apr 2024 14:19:34 +0800 Subject: [PATCH 1/2] Add more ut about create sendable NameDictionary Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I9F55J?from=project-issue Signed-off-by: liu-zelin Change-Id: Ifffaa3d05a9025bd0cb3dfdebd8936b969329eec --- .../napi/test/jsnapi_sendable_tests.cpp | 134 +++++++++++++++++- 1 file changed, 132 insertions(+), 2 deletions(-) diff --git a/ecmascript/napi/test/jsnapi_sendable_tests.cpp b/ecmascript/napi/test/jsnapi_sendable_tests.cpp index 9250a6882b..4e826cb5e9 100644 --- a/ecmascript/napi/test/jsnapi_sendable_tests.cpp +++ b/ecmascript/napi/test/jsnapi_sendable_tests.cpp @@ -101,11 +101,31 @@ panda::JSValueRef FunctionCallback(JsiRuntimeCallInfo *info) return **thisRef; } -Local GetNewSendableClassFunction( - EcmaVM *vm, const char *instanceKey, const char *staticKey, const char *nonStaticKey, Local parent) +Local GetNewSendableClassFunction(EcmaVM *vm, + const char *instanceKey, + const char *staticKey, + const char *nonStaticKey, + Local parent, + bool isDict = false) { FunctionRef::SendablePropertiesInfos infos; + if (isDict) { + uint32_t maxInline = std::max(JSSharedFunction::MAX_INLINE, JSSharedObject::MAX_INLINE); + for (uint32_t i = 0; i < maxInline; ++i) { + Local tempStr = StringRef::NewFromUtf8(vm, std::to_string(i).c_str()); + infos.instancePropertiesInfo.keys.push_back(tempStr); + infos.instancePropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); + infos.instancePropertiesInfo.attributes.push_back(PropertyAttribute(tempStr, true, true, true)); + infos.staticPropertiesInfo.keys.push_back(tempStr); + infos.staticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); + infos.staticPropertiesInfo.attributes.push_back(PropertyAttribute(tempStr, true, true, true)); + infos.nonStaticPropertiesInfo.keys.push_back(tempStr); + infos.nonStaticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); + infos.nonStaticPropertiesInfo.attributes.push_back(PropertyAttribute(tempStr, true, true, true)); + } + } + Local instanceStr = StringRef::NewFromUtf8(vm, instanceKey); infos.instancePropertiesInfo.keys.push_back(instanceStr); infos.instancePropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); @@ -186,6 +206,41 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionProperties) ASSERT_EQ("undefined", prototype->Get(vm_, invalidKey)->ToString(vm_)->ToString()); } +HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictProperties) +{ + LocalScope scope(vm_); + Local constructor = + GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", FunctionRef::Null(vm_), true); + Local prototype = constructor->GetFunctionPrototype(vm_); + + ASSERT_EQ("static", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString()); + ASSERT_EQ("nonStatic", prototype->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + ASSERT_EQ("undefined", constructor->Get(vm_, invalidKey)->ToString(vm_)->ToString()); + ASSERT_EQ("undefined", prototype->Get(vm_, invalidKey)->ToString(vm_)->ToString()); + + // set static property on constructor + constructor->Set(vm_, staticKey, StringRef::NewFromUtf8(vm_, "static0")); + ASSERT_EQ("static0", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString()); + + // set non static property on prototype + prototype->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic0")); + ASSERT_EQ("nonStatic0", prototype->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + + // set invalid property on constructor + ASSERT_FALSE(vm_->GetJSThread()->HasPendingException()); + constructor->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid")); + ASSERT_TRUE(vm_->GetJSThread()->HasPendingException()); + JSNApi::GetAndClearUncaughtException(vm_); + ASSERT_EQ("undefined", constructor->Get(vm_, invalidKey)->ToString(vm_)->ToString()); + + // set invalid property on prototype + ASSERT_FALSE(vm_->GetJSThread()->HasPendingException()); + prototype->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid")); + ASSERT_TRUE(vm_->GetJSThread()->HasPendingException()); + JSNApi::GetAndClearUncaughtException(vm_); + ASSERT_EQ("undefined", prototype->Get(vm_, invalidKey)->ToString(vm_)->ToString()); +} + HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInstance) { LocalScope scope(vm_); @@ -230,6 +285,50 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInstance) ASSERT_EQ("undefined", obj->Get(vm_, invalidKey)->ToString(vm_)->ToString()); } +HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInstance) +{ + LocalScope scope(vm_); + Local constructor = + GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", FunctionRef::Null(vm_), true); + Local argv[1] = {NumberRef::New(vm_, 0)}; + Local obj = constructor->Constructor(vm_, argv, 0); + Local obj0 = constructor->Constructor(vm_, argv, 0); + + ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(constructor))); + ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj0), JSNApiHelper::ToJSHandle(constructor))); + + // set instance property + ASSERT_EQ("undefined", obj->Get(vm_, instanceKey)->ToString(vm_)->ToString()); + ASSERT_EQ("undefined", obj0->Get(vm_, instanceKey)->ToString(vm_)->ToString()); + obj->Set(vm_, instanceKey, StringRef::NewFromUtf8(vm_, "instance")); + ASSERT_EQ("instance", obj->Get(vm_, instanceKey)->ToString(vm_)->ToString()); + ASSERT_EQ("undefined", obj0->Get(vm_, instanceKey)->ToString(vm_)->ToString()); + + // set non static property on prototype and get from instance + ASSERT_EQ("nonStatic", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + ASSERT_EQ("nonStatic", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + Local prototype = obj->GetPrototype(vm_); + prototype->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic0")); + ASSERT_EQ("nonStatic0", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + ASSERT_EQ("nonStatic0", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + + // set non static property on instance + ASSERT_FALSE(vm_->GetJSThread()->HasPendingException()); + obj->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic1")); + ASSERT_TRUE(vm_->GetJSThread()->HasPendingException()); + JSNApi::GetAndClearUncaughtException(vm_); + ASSERT_EQ("nonStatic0", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + ASSERT_EQ("nonStatic0", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString()); + + // set invalid property on instance + ASSERT_EQ("undefined", obj->Get(vm_, invalidKey)->ToString(vm_)->ToString()); + ASSERT_FALSE(vm_->GetJSThread()->HasPendingException()); + obj->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid")); + ASSERT_TRUE(vm_->GetJSThread()->HasPendingException()); + JSNApi::GetAndClearUncaughtException(vm_); + ASSERT_EQ("undefined", obj->Get(vm_, invalidKey)->ToString(vm_)->ToString()); +} + HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInherit) { LocalScope scope(vm_); @@ -261,6 +360,37 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInherit) ASSERT_EQ("parentNonStatic", obj->Get(vm_, parentNonStaticKey)->ToString(vm_)->ToString()); } +HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInherit) +{ + LocalScope scope(vm_); + Local parent = GetNewSendableClassFunction( + vm_, "parentInstance", "parentStatic", "parentNonStatic", FunctionRef::Null(vm_), true); + Local constructor = GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", parent); + Local argv[1] = {NumberRef::New(vm_, 0)}; + Local obj = constructor->Constructor(vm_, argv, 0); + Local obj0 = constructor->Constructor(vm_, argv, 0); + + ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(parent))); + ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj0), JSNApiHelper::ToJSHandle(parent))); + + // set parent instance property on instance + Local parentInstanceKey = StringRef::NewFromUtf8(vm_, "parentInstance"); + ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); + ASSERT_FALSE(vm_->GetJSThread()->HasPendingException()); + obj->Set(vm_, parentInstanceKey, StringRef::NewFromUtf8(vm_, "parentInstance")); + ASSERT_TRUE(vm_->GetJSThread()->HasPendingException()); + JSNApi::GetAndClearUncaughtException(vm_); + ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); + + // get parent static property from constructor + Local parentStaticKey = StringRef::NewFromUtf8(vm_, "parentStatic"); + ASSERT_EQ("parentStatic", constructor->Get(vm_, parentStaticKey)->ToString(vm_)->ToString()); + + // get parent non static property form instance + Local parentNonStaticKey = StringRef::NewFromUtf8(vm_, "parentNonStatic"); + ASSERT_EQ("parentNonStatic", obj->Get(vm_, parentNonStaticKey)->ToString(vm_)->ToString()); +} + HWTEST_F_L0(JSNApiTests, NewSendable) { LocalScope scope(vm_); From a6e3323370ad65912a5bfd21d0d429e24f1d6a34 Mon Sep 17 00:00:00 2001 From: liu-zelin Date: Sat, 20 Apr 2024 10:26:26 +0800 Subject: [PATCH 2/2] Can not modify properties inherit from sendable parent in napi Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I9I7BX?from=project-issue Signed-off-by: liu-zelin Change-Id: I302a4ce7c4ef074c0d96a98e5881c34661899a84 --- ecmascript/js_hclass.cpp | 101 +++++++++++++++--- ecmascript/js_hclass.h | 11 +- ecmascript/napi/jsnapi_expo.cpp | 14 ++- .../napi/test/jsnapi_sendable_tests.cpp | 88 +++++++++------ 4 files changed, 161 insertions(+), 53 deletions(-) diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp index b00ab401ce..988a236bca 100644 --- a/ecmascript/js_hclass.cpp +++ b/ecmascript/js_hclass.cpp @@ -1377,6 +1377,7 @@ PropertyLookupResult JSHClass::LookupPropertyInBuiltinHClass(const JSThread *thr JSHandle JSHClass::CreateSHClass(JSThread *thread, const std::vector &descs, + const JSHClass *parentHClass, bool isFunction) { EcmaVM *vm = thread->GetEcmaVM(); @@ -1385,18 +1386,22 @@ JSHandle JSHClass::CreateSHClass(JSThread *thread, uint32_t length = descs.size(); uint32_t maxInline = isFunction ? JSSharedFunction::MAX_INLINE : JSSharedObject::MAX_INLINE; + if (parentHClass) { + if (parentHClass->IsDictionaryMode()) { + auto dict = reinterpret_cast(parentHClass->GetLayout().GetTaggedObject()); + length += dict->EntriesCount(); + } else { + length += parentHClass->NumberOfProps(); + } + } + JSHandle hclass = isFunction ? factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, length) : factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length); if (LIKELY(length <= maxInline)) { - auto layout = CreateSInlinedLayout(thread, descs); - hclass->SetLayout(thread, layout); - hclass->SetNumberOfProps(length); + CreateSInlinedLayout(thread, descs, hclass, parentHClass); } else { - auto layout = CreateSDictLayout(thread, descs); - hclass->SetLayout(thread, layout); - hclass->SetNumberOfProps(0); - hclass->SetIsDictionaryMode(true); + CreateSDictLayout(thread, descs, hclass, parentHClass); } return hclass; @@ -1404,7 +1409,7 @@ JSHandle JSHClass::CreateSHClass(JSThread *thread, JSHandle JSHClass::CreateSConstructorHClass(JSThread *thread, const std::vector &descs) { - auto hclass = CreateSHClass(thread, descs, true); + auto hclass = CreateSHClass(thread, descs, nullptr, true); hclass->SetClassConstructor(true); hclass->SetConstructor(true); return hclass; @@ -1418,15 +1423,22 @@ JSHandle JSHClass::CreateSPrototypeHClass(JSThread *thread, const std: return hclass; } -JSHandle JSHClass::CreateSInlinedLayout(JSThread *thread, const std::vector &descs) +void JSHClass::CreateSInlinedLayout(JSThread *thread, + const std::vector &descs, + const JSHandle &hclass, + const JSHClass *parentHClass) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); + uint32_t parentLength{0}; + if (parentHClass) { + parentLength = parentHClass->NumberOfProps(); + } auto length = descs.size(); - auto layout = factory->CreateSLayoutInfo(length); - JSMutableHandle key(thread, JSTaggedValue::Undefined()); + auto layout = factory->CreateSLayoutInfo(length + parentLength); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < length; ++i) { key.Update(descs[i].GetKey()); PropertyAttributes attr = @@ -1441,16 +1453,50 @@ JSHandle JSHClass::CreateSInlinedLayout(JSThread *thread, const std: layout->AddKey(thread, i, key.GetTaggedValue(), attr); } - return layout; + auto index = length; + if (parentHClass) { + JSHandle old(thread, parentHClass->GetLayout()); + for (uint32_t i = 0; i < parentLength; i++) { + key.Update(descs[i].GetKey()); + auto entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index); + if (entry != -1) { + continue; + } + auto attr = PropertyAttributes(old->GetAttr(i)); + attr.SetOffset(index); + layout->AddKey(thread, index, old->GetKey(i), attr); + ++index; + } + } + + hclass->SetLayout(thread, layout); + hclass->SetNumberOfProps(index); + auto inlinedPropsLength = hclass->GetInlinedProperties(); + if (inlinedPropsLength > index) { + uint32_t duplicatedSize = (inlinedPropsLength - index) * JSTaggedValue::TaggedTypeSize(); + hclass->SetObjectSize(hclass->GetObjectSize() - duplicatedSize); + } } -JSHandle JSHClass::CreateSDictLayout(JSThread *thread, const std::vector &descs) +void JSHClass::CreateSDictLayout(JSThread *thread, + const std::vector &descs, + const JSHandle &hclass, + const JSHClass *parentHClass) { + uint32_t parentLength{0}; + if (parentHClass) { + if (parentHClass->IsDictionaryMode()) { + parentLength = + reinterpret_cast(parentHClass->GetLayout().GetTaggedObject())->EntriesCount(); + } else { + parentLength = parentHClass->NumberOfProps(); + } + } auto length = descs.size(); JSMutableHandle dict( - thread, NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(length))); + thread, + NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(length + parentLength))); JSMutableHandle key(thread, JSTaggedValue::Undefined()); - auto globalConst = const_cast(thread->GlobalConstants()); JSHandle value = globalConst->GetHandledUndefined(); @@ -1464,7 +1510,30 @@ JSHandle JSHClass::CreateSDictLayout(JSThread *thread, const std dict.Update(newDict); } - return dict; + if (parentHClass) { + if (parentHClass->IsDictionaryMode()) { + JSHandle old(thread, parentHClass->GetLayout()); + std::vector indexOrder = old->GetEnumerationOrder(); + for (uint32_t i = 0; i < parentLength; i++) { + key.Update(old->GetKey(indexOrder[i])); + JSHandle newDict = NameDictionary::Put( + thread, dict, key, value, PropertyAttributes(old->GetAttributes(indexOrder[i]))); + dict.Update(newDict); + } + } else { + JSHandle old(thread, parentHClass->GetLayout()); + for (uint32_t i = 0; i < parentLength; i++) { + key.Update(old->GetKey(i)); + JSHandle newDict = + NameDictionary::Put(thread, dict, key, value, PropertyAttributes(old->GetAttr(i))); + dict.Update(newDict); + } + } + } + + hclass->SetLayout(thread, dict); + hclass->SetNumberOfProps(0); + hclass->SetIsDictionaryMode(true); } } // namespace panda::ecmascript diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 4330e67df8..6b107e9ae2 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -1966,13 +1966,20 @@ public: static JSHandle CreateSHClass(JSThread *thread, const std::vector &descs, + const JSHClass *parentHClass = nullptr, bool isFunction = false); static JSHandle CreateSConstructorHClass(JSThread *thread, const std::vector &descs); static JSHandle CreateSPrototypeHClass(JSThread *thread, const std::vector &descs); private: - static JSHandle CreateSInlinedLayout(JSThread *thread, const std::vector &descs); - static JSHandle CreateSDictLayout(JSThread *thread, const std::vector &descs); + static void CreateSInlinedLayout(JSThread *thread, + const std::vector &descs, + const JSHandle &hclass, + const JSHClass *parentHClass = nullptr); + static void CreateSDictLayout(JSThread *thread, + const std::vector &descs, + const JSHandle &hclass, + const JSHClass *parentHClass = nullptr); static inline void AddTransitions(const JSThread *thread, const JSHandle &parent, const JSHandle &child, const JSHandle &key, PropertyAttributes attr); diff --git a/ecmascript/napi/jsnapi_expo.cpp b/ecmascript/napi/jsnapi_expo.cpp index 69b838f662..f1eba58fd6 100644 --- a/ecmascript/napi/jsnapi_expo.cpp +++ b/ecmascript/napi/jsnapi_expo.cpp @@ -2417,6 +2417,7 @@ Local FunctionRef::NewSendableClassFunction(const EcmaVM *vm, EscapeLocalScope scope(vm); ObjectFactory *factory = vm->GetFactory(); + bool hasParent = !parent->IsNull(); JSNapiSendable sendable(thread, infos, name); JSHandle prototypeHClass = JSHClass::CreateSPrototypeHClass(thread, sendable.GetNonStaticDescs()); JSHandle prototype = factory->NewSharedOldSpaceJSObject(prototypeHClass); @@ -2429,23 +2430,28 @@ Local FunctionRef::NewSendableClassFunction(const EcmaVM *vm, JSObject::SetSProperties(thread, JSHandle::Cast(constructor), constructorHClass, sendable.GetStaticDescs()); - if (!parent->IsNull()) { + if (hasParent) { auto parentPrototype = parent->GetFunctionPrototype(vm); prototypeHClass->SetPrototype(thread, JSNApiHelper::ToJSHandle(parentPrototype)); constructorHClass->SetPrototype(thread, JSNApiHelper::ToJSHandle(parent)); } - prototype->GetJSHClass()->SetExtensible(false); + prototypeHClass->SetExtensible(false); constructor->SetHomeObject(thread, prototype); constructor->SetProtoOrHClass(thread, prototype); constructor->SetLexicalEnv(thread, constructor); constructor->SetCallNapi(callNapi); constructor->SetSFunctionExtraInfo(thread, nullptr, deleter, data, nativeBindingSize); - JSHandle iHClass = JSHClass::CreateSHClass(thread, sendable.GetInstanceDescs()); + JSHClass *parentIHClass{nullptr}; + if (hasParent) { + JSHandle parentHandle(JSNApiHelper::ToJSHandle(parent)); + parentIHClass = reinterpret_cast(parentHandle->GetProtoOrHClass().GetTaggedObject()); + } + JSHandle iHClass = JSHClass::CreateSHClass(thread, sendable.GetInstanceDescs(), parentIHClass); iHClass->SetPrototype(thread, JSHandle(prototype)); iHClass->SetExtensible(false); constructor->SetProtoOrHClass(thread, iHClass); - constructor->GetJSHClass()->SetExtensible(false); + constructorHClass->SetExtensible(false); Local result = JSNApiHelper::ToLocal(JSHandle(constructor)); return scope.Escape(result); diff --git a/ecmascript/napi/test/jsnapi_sendable_tests.cpp b/ecmascript/napi/test/jsnapi_sendable_tests.cpp index 4e826cb5e9..019ae5ed4e 100644 --- a/ecmascript/napi/test/jsnapi_sendable_tests.cpp +++ b/ecmascript/napi/test/jsnapi_sendable_tests.cpp @@ -102,11 +102,9 @@ panda::JSValueRef FunctionCallback(JsiRuntimeCallInfo *info) } Local GetNewSendableClassFunction(EcmaVM *vm, - const char *instanceKey, - const char *staticKey, - const char *nonStaticKey, Local parent, - bool isDict = false) + bool isDict = false, + bool duplicated = false) { FunctionRef::SendablePropertiesInfos infos; @@ -126,21 +124,39 @@ Local GetNewSendableClassFunction(EcmaVM *vm, } } - Local instanceStr = StringRef::NewFromUtf8(vm, instanceKey); + std::string instanceKey = "instance"; + std::string staticKey = "static"; + std::string nonStaticKey = "nonStatic"; + + if (!parent->IsNull()) { + instanceKey = "parentInstance"; + staticKey = "parentStatic"; + nonStaticKey = "parentNonStatic"; + } + + Local instanceStr = StringRef::NewFromUtf8(vm, instanceKey.c_str()); infos.instancePropertiesInfo.keys.push_back(instanceStr); infos.instancePropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); infos.instancePropertiesInfo.attributes.push_back(PropertyAttribute(instanceStr, true, true, true)); - Local staticStr = StringRef::NewFromUtf8(vm, staticKey); + Local staticStr = StringRef::NewFromUtf8(vm, staticKey.c_str()); infos.staticPropertiesInfo.keys.push_back(staticStr); infos.staticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); infos.staticPropertiesInfo.attributes.push_back(PropertyAttribute(staticStr, true, true, true)); - Local nonStaticStr = StringRef::NewFromUtf8(vm, nonStaticKey); + Local nonStaticStr = StringRef::NewFromUtf8(vm, nonStaticKey.c_str()); infos.nonStaticPropertiesInfo.keys.push_back(nonStaticStr); infos.nonStaticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); infos.nonStaticPropertiesInfo.attributes.push_back(PropertyAttribute(nonStaticStr, true, true, true)); + if (duplicated) { + Local duplicatedKey = StringRef::NewFromUtf8(vm, "instance"); + Local duplicatedValue = NumberRef::New(vm, 0); + infos.instancePropertiesInfo.keys.push_back(duplicatedKey); + infos.instancePropertiesInfo.types.push_back(FunctionRef::SendableType::NONE); + infos.instancePropertiesInfo.attributes.push_back(PropertyAttribute(duplicatedValue, true, true, true)); + } + Local constructor = FunctionRef::NewSendableClassFunction( vm, FunctionCallback, nullptr, nullptr, StringRef::NewFromUtf8(vm, "name"), infos, parent); @@ -150,8 +166,7 @@ Local GetNewSendableClassFunction(EcmaVM *vm, HWTEST_F_L0(JSNApiTests, NewSendableClassFunction) { LocalScope scope(vm_); - Local constructor = - GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", FunctionRef::Null(vm_)); + Local constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_)); ASSERT_EQ("name", constructor->GetName(vm_)->ToString()); ASSERT_TRUE(constructor->IsFunction()); @@ -174,8 +189,7 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunction) HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionProperties) { LocalScope scope(vm_); - Local constructor = - GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", FunctionRef::Null(vm_)); + Local constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_)); Local prototype = constructor->GetFunctionPrototype(vm_); ASSERT_EQ("static", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString()); @@ -209,8 +223,7 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionProperties) HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictProperties) { LocalScope scope(vm_); - Local constructor = - GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", FunctionRef::Null(vm_), true); + Local constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), true); Local prototype = constructor->GetFunctionPrototype(vm_); ASSERT_EQ("static", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString()); @@ -244,8 +257,7 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictProperties) HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInstance) { LocalScope scope(vm_); - Local constructor = - GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", FunctionRef::Null(vm_)); + Local constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_)); Local argv[1] = {NumberRef::New(vm_, 0)}; Local obj = constructor->Constructor(vm_, argv, 0); Local obj0 = constructor->Constructor(vm_, argv, 0); @@ -288,8 +300,7 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInstance) HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInstance) { LocalScope scope(vm_); - Local constructor = - GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", FunctionRef::Null(vm_), true); + Local constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), true); Local argv[1] = {NumberRef::New(vm_, 0)}; Local obj = constructor->Constructor(vm_, argv, 0); Local obj0 = constructor->Constructor(vm_, argv, 0); @@ -332,9 +343,8 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInstance) HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInherit) { LocalScope scope(vm_); - Local parent = - GetNewSendableClassFunction(vm_, "parentInstance", "parentStatic", "parentNonStatic", FunctionRef::Null(vm_)); - Local constructor = GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", parent); + Local parent = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_)); + Local constructor = GetNewSendableClassFunction(vm_, parent); Local argv[1] = {NumberRef::New(vm_, 0)}; Local obj = constructor->Constructor(vm_, argv, 0); Local obj0 = constructor->Constructor(vm_, argv, 0); @@ -345,11 +355,8 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInherit) // set parent instance property on instance Local parentInstanceKey = StringRef::NewFromUtf8(vm_, "parentInstance"); ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); - ASSERT_FALSE(vm_->GetJSThread()->HasPendingException()); obj->Set(vm_, parentInstanceKey, StringRef::NewFromUtf8(vm_, "parentInstance")); - ASSERT_TRUE(vm_->GetJSThread()->HasPendingException()); - JSNApi::GetAndClearUncaughtException(vm_); - ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); + ASSERT_EQ("parentInstance", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); // get parent static property from constructor Local parentStaticKey = StringRef::NewFromUtf8(vm_, "parentStatic"); @@ -363,9 +370,8 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInherit) HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInherit) { LocalScope scope(vm_); - Local parent = GetNewSendableClassFunction( - vm_, "parentInstance", "parentStatic", "parentNonStatic", FunctionRef::Null(vm_), true); - Local constructor = GetNewSendableClassFunction(vm_, "instance", "static", "nonStatic", parent); + Local parent = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), true); + Local constructor = GetNewSendableClassFunction(vm_, parent); Local argv[1] = {NumberRef::New(vm_, 0)}; Local obj = constructor->Constructor(vm_, argv, 0); Local obj0 = constructor->Constructor(vm_, argv, 0); @@ -376,11 +382,8 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInherit) // set parent instance property on instance Local parentInstanceKey = StringRef::NewFromUtf8(vm_, "parentInstance"); ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); - ASSERT_FALSE(vm_->GetJSThread()->HasPendingException()); obj->Set(vm_, parentInstanceKey, StringRef::NewFromUtf8(vm_, "parentInstance")); - ASSERT_TRUE(vm_->GetJSThread()->HasPendingException()); - JSNApi::GetAndClearUncaughtException(vm_); - ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); + ASSERT_EQ("parentInstance", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); // get parent static property from constructor Local parentStaticKey = StringRef::NewFromUtf8(vm_, "parentStatic"); @@ -391,6 +394,29 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInherit) ASSERT_EQ("parentNonStatic", obj->Get(vm_, parentNonStaticKey)->ToString(vm_)->ToString()); } +HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInheritWithDuplicatedKey) +{ + LocalScope scope(vm_); + Local parent = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_)); + Local constructor = GetNewSendableClassFunction(vm_, parent, false, true); + Local argv[1] = {NumberRef::New(vm_, 0)}; + Local obj = constructor->Constructor(vm_, argv, 0); + + ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(parent))); + + // set parent instance property on instance + Local parentInstanceKey = StringRef::NewFromUtf8(vm_, "parentInstance"); + ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); + obj->Set(vm_, parentInstanceKey, StringRef::NewFromUtf8(vm_, "parentInstance")); + ASSERT_EQ("parentInstance", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString()); + + // set duplicated instance property on instance + Local duplicatedInstanceKey = StringRef::NewFromUtf8(vm_, "instance"); + ASSERT_EQ("undefined", obj->Get(vm_, duplicatedInstanceKey)->ToString(vm_)->ToString()); + obj->Set(vm_, duplicatedInstanceKey, NumberRef::New(vm_, 1)); + ASSERT_EQ("1", obj->Get(vm_, duplicatedInstanceKey)->ToString(vm_)->ToString()); +} + HWTEST_F_L0(JSNApiTests, NewSendable) { LocalScope scope(vm_);