diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp index dd2771fb73..1c1c8d4d82 100644 --- a/ecmascript/js_hclass.cpp +++ b/ecmascript/js_hclass.cpp @@ -1401,6 +1401,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(); @@ -1409,18 +1410,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; @@ -1428,7 +1433,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; @@ -1442,15 +1447,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 = @@ -1465,16 +1477,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(); @@ -1488,7 +1534,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 9c0340ee55..40ec7767e7 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -1999,13 +1999,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 5a7500c887..2175127fa5 100644 --- a/ecmascript/napi/jsnapi_expo.cpp +++ b/ecmascript/napi/jsnapi_expo.cpp @@ -2926,6 +2926,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); @@ -2937,23 +2938,28 @@ Local FunctionRef::NewSendableClassFunction(const EcmaVM *vm, JSObject::SetSProperties(thread, prototype, sendable.GetNonStaticDescs()); JSObject::SetSProperties(thread, JSHandle::Cast(constructor), 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 d97c21eb80..0abdd396fa 100644 --- a/ecmascript/napi/test/jsnapi_sendable_tests.cpp +++ b/ecmascript/napi/test/jsnapi_sendable_tests.cpp @@ -101,26 +101,62 @@ 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, + Local parent, + bool isDict = false, + bool duplicated = false) { FunctionRef::SendablePropertiesInfos infos; - Local instanceStr = StringRef::NewFromUtf8(vm, instanceKey); + 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)); + } + } + + 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); @@ -130,8 +166,7 @@ Local GetNewSendableClassFunction( 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()); @@ -154,8 +189,41 @@ 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()); + 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, NewSendableClassFunctionDictProperties) +{ + LocalScope scope(vm_); + Local constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), true); Local prototype = constructor->GetFunctionPrototype(vm_); ASSERT_EQ("static", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString()); @@ -189,8 +257,50 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionProperties) 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); + + 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, NewSendableClassFunctionDictInstance) +{ + LocalScope scope(vm_); + 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); @@ -233,9 +343,8 @@ HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInstance) 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); @@ -246,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"); @@ -261,6 +367,56 @@ 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_, 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); + + 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()); + obj->Set(vm_, parentInstanceKey, StringRef::NewFromUtf8(vm_, "parentInstance")); + ASSERT_EQ("parentInstance", 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, 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_);