diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index 84208d9d4f13ec3ff36a8c77c3454c49705a8f45..422d33e7b95de0786d204a6032eee8f59f37bb6b 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -58,7 +58,9 @@ #include "ecmascript/builtins/builtins_weak_ref.h" #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/containers/containers_private.h" +#include "ecmascript/containers/containers_treeset.h" #include "ecmascript/dfx/native_module_failure_info.h" +#include "ecmascript/global_env.h" #include "ecmascript/js_arraybuffer.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_async_function.h" @@ -94,6 +96,8 @@ #include "ecmascript/builtins/builtins_segmenter.h" #include "ecmascript/builtins/builtins_segments.h" #include "ecmascript/builtins/builtins_segment_iterator.h" +#include "ecmascript/js_api/js_api_tree_set_iterator.h" +#include "ecmascript/js_api/js_api_tree_set.h" #include "ecmascript/js_collator.h" #include "ecmascript/js_date_time_format.h" #include "ecmascript/js_displaynames.h" @@ -340,6 +344,7 @@ void Builtins::Initialize(const JSHandle &env, JSThread *thread, bool InitializeAsyncGeneratorFunction(env, objFuncClass); InitializePromise(env, objFuncClass); InitializePromiseJob(env); + InitializeTreeSet(env); thread->CheckSafepointIfSuspended(); #ifdef ARK_SUPPORT_INTL InitializeIntl(env, objFuncPrototypeVal); @@ -3816,6 +3821,104 @@ JSHandle Builtins::InitializeArkPrivate(const JSHandle &env return arkPrivate; } +JSHandle Builtins::NewContainerConstructor(const JSHandle &env, + const JSHandle &prototype, EcmaEntrypoint ctorFunc, const char *name, int length) +{ + JSHandle ctor = + factory_->NewJSBuiltinFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); + + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromASCII(name)); + JSFunction::SetFunctionName(thread_, JSHandle(ctor), nameString, + globalConst->GetHandledUndefined()); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor1(thread_, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, prototype, constructorKey, descriptor1); + + /* set "prototype" in constructor */ + JSFunction::SetFunctionPrototypeOrInstanceHClass(thread_, ctor, prototype.GetTaggedValue()); + + return ctor; +} + +void Builtins::InitializeTreeSetIterator(const JSHandle &env) +{ + // Iterator.hclass + JSHandle iteratorClass = + factory_->NewEcmaHClass(JSObject::SIZE, JSType::JS_ITERATOR, env->GetIteratorPrototype()); + + JSHandle setIteratorPrototype(factory_->NewJSObject(iteratorClass)); + + SetFrozenFunction(env, setIteratorPrototype, "next", JSAPITreeSetIterator::Next, containers::FuncLength::ZERO); + SetStringTagSymbol(env, setIteratorPrototype, "TreeSet Iterator"); + env->SetTreeSetIteratorPrototype(thread_, setIteratorPrototype); +} + +JSHandle Builtins::InitializeTreeSet(const JSHandle &env) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // TreeSet.prototype + JSHandle setFuncPrototype = factory_->NewEmptyJSObject(env); + JSHandle setFuncPrototypeValue(setFuncPrototype); + // TreeSet.prototype_or_hclass + JSHandle setInstanceClass = + factory_->NewEcmaHClass(JSAPITreeSet::SIZE, JSType::JS_API_TREE_SET, setFuncPrototypeValue); + // TreeSet() = new Function() + JSHandle setFunction(NewContainerConstructor( + env, setFuncPrototype, containers::ContainersTreeSet::TreeSetConstructor, + "TreeSet", containers::FuncLength::ZERO)); + JSFunction::SetFunctionPrototypeOrInstanceHClass(thread_, + JSHandle::Cast(setFunction), setInstanceClass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(setFuncPrototype), constructorKey, setFunction); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + + // TreeSet.prototype methods (excluding constructor and '@@' internal properties) + + for (const base::BuiltinFunctionEntry &entry: containers::ContainersTreeSet::GetTreeSetPrototypeFunctions()) { + SetFrozenFunction(env, setFuncPrototype, entry.GetName().data(), entry.GetEntrypoint(), + entry.GetLength()); + } + + // @@ToStringTag + SetStringTagSymbol(env, setFuncPrototype, "TreeSet"); + // %TreeSetPrototype% [ @@iterator ] + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle values(thread_, globalConst->GetValuesString()); + JSHandle valuesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(setFuncPrototype), values); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + PropertyDescriptor descriptor(thread_, valuesFunc, false, false, false); + JSObject::DefineOwnProperty(thread_, setFuncPrototype, iteratorSymbol, descriptor); + // length + JSHandle lengthGetter = + CreateGetter(env, containers::ContainersTreeSet::GetLength, "length", containers::FuncLength::ZERO); + JSHandle lengthKey(thread_, globalConst->GetLengthString()); + SetGetter(setFuncPrototype, lengthKey, lengthGetter); + + InitializeTreeSetIterator(env); + + JSHandle undefinedHandle = globalConst->GetHandledUndefined(); + // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%). + JSHandle klass = JSHandle::Cast(env->GetIteratorResultClass()); + JSHandle undefinedIteratorResult = factory_->NewJSObject(klass); + + // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value). + // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done). + undefinedIteratorResult->SetPropertyInlinedPropsWithSize( + thread_, undefinedHandle.GetTaggedValue()); + undefinedIteratorResult->SetPropertyInlinedPropsWithSize( + thread_, JSTaggedValue(true)); + + env->SetUndefinedIteratorResult(thread_, undefinedIteratorResult); + + env->SetTreeSetConstructor(thread_, setFunction); + return setFunction; +} + void Builtins::InitializeModuleNamespace(const JSHandle &env, const JSHandle &objFuncClass) const { diff --git a/ecmascript/builtins/builtins.h b/ecmascript/builtins/builtins.h index 392ab53490ddd6d43234f195957acbbb40457799..ef53d02e6b6e14e9b5253cfb9de5516bb0dd89ea 100644 --- a/ecmascript/builtins/builtins.h +++ b/ecmascript/builtins/builtins.h @@ -407,6 +407,12 @@ private: void InitializeGlobalRegExp(JSHandle &obj) const; // Using to initialize jsapi container JSHandle InitializeArkPrivate(const JSHandle &env) const; + + JSHandle NewContainerConstructor(const JSHandle &env, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length); + void InitializeTreeSetIterator(const JSHandle &env); + JSHandle InitializeTreeSet(const JSHandle &env); + void SetConstantObject(const JSHandle &obj, std::string_view key, JSHandle &value) const; void SetFrozenFunction(const JSHandle &env, const JSHandle &obj, std::string_view key, diff --git a/ecmascript/compiler/aot_file/aot_version.h b/ecmascript/compiler/aot_file/aot_version.h index 14cbae58ea52d0e2d6653a3eae0313b7525d0932..f5b1eaae3a6c1d47110c10c84403dbb5ed698c14 100644 --- a/ecmascript/compiler/aot_file/aot_version.h +++ b/ecmascript/compiler/aot_file/aot_version.h @@ -25,9 +25,9 @@ public: // Release Version Snapshot Version // 3.2 0.0.0.x // 4.0 4.0.0.x - static constexpr base::FileHeaderBase::VersionType AN_VERSION = {4, 0, 1, 7}; + static constexpr base::FileHeaderBase::VersionType AN_VERSION = {4, 0, 1, 8}; static constexpr bool AN_STRICT_MATCH = true; - static constexpr base::FileHeaderBase::VersionType AI_VERSION = {4, 0, 1, 3}; + static constexpr base::FileHeaderBase::VersionType AI_VERSION = {4, 0, 1, 4}; static constexpr bool AI_STRICT_MATCH = true; constexpr static const int VERSION_MULTIPLE_SIZE = 2; static PUBLIC_API std::string GetAOTVersion() diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp index d5f7362558f144fbbd42350c60f9f29d373853fe..950f97a240856b42237624459814e7f1eb2a8435 100644 --- a/ecmascript/containers/containers_private.cpp +++ b/ecmascript/containers/containers_private.cpp @@ -70,11 +70,13 @@ JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) } // Lazy set an undefinedIteratorResult to global constants - auto globalConst = const_cast(thread->GlobalConstants()); - JSHandle undefinedHandle = globalConst->GetHandledUndefined(); - JSHandle undefinedIteratorResult = JSIterator::CreateIterResultObject(thread, undefinedHandle, true); - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - env->SetUndefinedIteratorResult(thread, undefinedIteratorResult); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + if (!env->GetUndefinedIteratorResult()->IsUndefined()) { + auto globalConst = const_cast(thread->GlobalConstants()); + JSHandle undefinedHandle = globalConst->GetHandledUndefined(); + JSHandle undefinedIteratorResult = JSIterator::CreateIterResultObject(thread, undefinedHandle, true); + env->SetUndefinedIteratorResult(thread, undefinedIteratorResult); + } JSTaggedValue res = JSTaggedValue::Undefined(); switch (tag) { @@ -111,7 +113,11 @@ JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) break; } case ContainerTag::TreeSet: { - res = InitializeContainer(thread, thisValue, InitializeTreeSet, "TreeSetConstructor"); + if (!env->GetTreeSetConstructor()->IsUndefined()) { + res = env->GetTreeSetConstructor().GetTaggedValue(); + } else { + std::abort(); + } break; } case ContainerTag::Vector: { @@ -496,70 +502,6 @@ void ContainersPrivate::InitializeTreeMapIterator(JSThread *thread) env->SetTreeMapIteratorPrototype(thread, mapIteratorPrototype); } -JSHandle ContainersPrivate::InitializeTreeSet(JSThread *thread) -{ - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - // TreeSet.prototype - JSHandle setFuncPrototype = factory->NewEmptyJSObject(); - JSHandle setFuncPrototypeValue(setFuncPrototype); - // TreeSet.prototype_or_hclass - JSHandle setInstanceClass = - factory->NewEcmaHClass(JSAPITreeSet::SIZE, JSType::JS_API_TREE_SET, setFuncPrototypeValue); - // TreeSet() = new Function() - JSHandle setFunction(NewContainerConstructor( - thread, setFuncPrototype, ContainersTreeSet::TreeSetConstructor, "TreeSet", FuncLength::ZERO)); - JSFunction::SetFunctionPrototypeOrInstanceHClass(thread, - JSHandle::Cast(setFunction), setInstanceClass.GetTaggedValue()); - - // "constructor" property on the prototype - JSHandle constructorKey = globalConst->GetHandledConstructorString(); - JSObject::SetProperty(thread, JSHandle(setFuncPrototype), constructorKey, setFunction); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - - // TreeSet.prototype methods (excluding constructor and '@@' internal properties) - for (const base::BuiltinFunctionEntry &entry: ContainersTreeSet::GetTreeSetPrototypeFunctions()) { - SetFrozenFunction(thread, setFuncPrototype, entry.GetName().data(), entry.GetEntrypoint(), - entry.GetLength(), entry.GetBuiltinStubId()); - } - - // @@ToStringTag - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - SetStringTagSymbol(thread, env, setFuncPrototype, "TreeSet"); - // %TreeSetPrototype% [ @@iterator ] - JSHandle iteratorSymbol = env->GetIteratorSymbol(); - JSHandle values(thread, globalConst->GetValuesString()); - JSHandle valuesFunc = - JSObject::GetMethod(thread, JSHandle::Cast(setFuncPrototype), values); - RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); - PropertyDescriptor descriptor(thread, valuesFunc, false, false, false); - JSObject::DefineOwnProperty(thread, setFuncPrototype, iteratorSymbol, descriptor); - // length - JSHandle lengthGetter = - CreateGetter(thread, ContainersTreeSet::GetLength, "length", FuncLength::ZERO); - JSHandle lengthKey(thread, globalConst->GetLengthString()); - SetGetter(thread, setFuncPrototype, lengthKey, lengthGetter); - - InitializeTreeSetIterator(thread); - return setFunction; -} - -void ContainersPrivate::InitializeTreeSetIterator(JSThread *thread) -{ - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - // Iterator.hclass - JSHandle iteratorClass = - factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_ITERATOR, env->GetIteratorPrototype()); - - // TreeSetIterator.prototype - JSHandle setIteratorPrototype(factory->NewJSObject(iteratorClass)); - // TreeSetIterator.prototype.next() - SetFrozenFunction(thread, setIteratorPrototype, "next", JSAPITreeSetIterator::Next, FuncLength::ZERO); - SetStringTagSymbol(thread, env, setIteratorPrototype, "TreeSet Iterator"); - env->SetTreeSetIteratorPrototype(thread, setIteratorPrototype); -} - JSHandle ContainersPrivate::InitializePlainArray(JSThread *thread) { auto globalConst = const_cast(thread->GlobalConstants()); diff --git a/ecmascript/containers/containers_private.h b/ecmascript/containers/containers_private.h index 4ca21e1e21a43c58f54238cb25130016e3bff837..b7df47b335c67aac678fc696ac7403e06a850685 100644 --- a/ecmascript/containers/containers_private.h +++ b/ecmascript/containers/containers_private.h @@ -85,8 +85,6 @@ private: static void InitializeLightWeightSetIterator(JSThread *thread); static JSHandle InitializeTreeMap(JSThread *thread); static void InitializeTreeMapIterator(JSThread *thread); - static JSHandle InitializeTreeSet(JSThread *thread); - static void InitializeTreeSetIterator(JSThread *thread); static JSHandle InitializePlainArray(JSThread *thread); static void InitializePlainArrayIterator(JSThread *thread); static JSHandle InitializeVector(JSThread *thread); diff --git a/ecmascript/global_env_fields.h b/ecmascript/global_env_fields.h index 16832416c892e764ff645d1504467cb24e34cea8..332962d141de31270f6e870cbbd6cd0d1cf0b160 100644 --- a/ecmascript/global_env_fields.h +++ b/ecmascript/global_env_fields.h @@ -270,7 +270,8 @@ V(JSTaggedValue, ElementHOLE_TAGGEDClass, ELEMENT_HOLE_TAGGED_HCLASS_INDEX) \ V(JSTaggedValue, ElementHOLE_TAGGEDProtoClass, ELEMENT_HOLE_TAGGED_PROTO_HCLASS_INDEX) \ V(JSTaggedValue, ModuleManagerNativePointer, MODULE_MANAGER_NATIVE_POINTER_INDEX) \ - V(JSTaggedValue, DetectorDependentInfos, DETECTOR_DEPENDENT_INFOS_INDEX) + V(JSTaggedValue, DetectorDependentInfos, DETECTOR_DEPENDENT_INFOS_INDEX) \ + V(JSTaggedValue, TreeSetConstructor, TREE_SET_CONSTRUCTOR) #define GLOBAL_ENV_CONTAINER_ITERATORS(V) \ /* non ECMA standard jsapi containers iterators */ \ diff --git a/ecmascript/js_type_metadata/global_env.json b/ecmascript/js_type_metadata/global_env.json index 2b1821995aa31d3f904f3892335b647d02d34aaa..2f6223170a650749c1a272e1f3ca71fce39a8589 100644 --- a/ecmascript/js_type_metadata/global_env.json +++ b/ecmascript/js_type_metadata/global_env.json @@ -1 +1 @@ -{"name": "GLOBAL_ENV", "offsets": [], "end_offset": 4368, "parents": ["TAGGED_ARRAY"]} +{"name": "GLOBAL_ENV", "offsets": [], "end_offset": 4376, "parents": ["TAGGED_ARRAY"]} diff --git a/ecmascript/serializer/base_deserializer.cpp b/ecmascript/serializer/base_deserializer.cpp index 87b99f11c2c8efec845ed9e210a4197fc14acc74..a6b0a48031f5da29b34172c90ad3c2f09599d319 100644 --- a/ecmascript/serializer/base_deserializer.cpp +++ b/ecmascript/serializer/base_deserializer.cpp @@ -582,6 +582,8 @@ JSTaggedType BaseDeserializer::RelocateObjectProtoAddr(uint8_t objectType) return env->GetSharedMapPrototype().GetTaggedType(); case (uint8_t)JSType::JS_SET: return env->GetSetPrototype().GetTaggedType(); + case (uint8_t)JSType::JS_API_TREE_SET: + return JSHandle(env->GetTreeSetConstructor())->GetFunctionPrototype(thread_).GetRawData(); case (uint8_t)JSType::JS_SHARED_SET: return env->GetSharedSetPrototype().GetTaggedType(); case (uint8_t)JSType::JS_SENDABLE_ARRAY_BUFFER: diff --git a/ecmascript/serializer/tests/serializer_test.cpp b/ecmascript/serializer/tests/serializer_test.cpp index 8773cf217afa8c5a6181ac59257de5f9907506aa..667eceacda268f301f3aa781928dd214d39f95b7 100644 --- a/ecmascript/serializer/tests/serializer_test.cpp +++ b/ecmascript/serializer/tests/serializer_test.cpp @@ -36,6 +36,16 @@ #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" +#include "ecmascript/containers/containers_private.h" +#include "ecmascript/containers/containers_treeset.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_api/js_api_tree_set.h" +#include "ecmascript/js_api/js_api_tree_set_iterator.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/containers/tests/containers_test_helper.h" + + #include "ecmascript/serializer/value_serializer.h" #include "ecmascript/serializer/base_deserializer.h" #include "ecmascript/serializer/module_deserializer.h" @@ -44,6 +54,7 @@ using namespace panda::ecmascript; using namespace testing::ext; using namespace panda::ecmascript::builtins; +using namespace panda::ecmascript::containers; namespace panda::test { using DeserializeFunc = void (*)(SerializeData* data); @@ -564,6 +575,270 @@ public: Destroy(); } + class JSTreeSetTestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSThread *thread = argv->GetThread(); + JSHandle value = GetCallArg(argv, 0); + JSHandle key = GetCallArg(argv, 1); + JSHandle set(GetCallArg(argv, 2)); // 2 means the second arg + EXPECT_EQ(key.GetTaggedValue(), value.GetTaggedValue()); + JSAPITreeSet::Delete(thread, set, key); + + JSHandle jsTreeSet(GetThis(argv)); + JSAPITreeSet::Add(thread, jsTreeSet, key); + return JSTaggedValue::Undefined(); + } + + static JSTaggedValue TestCompareFunction(EcmaRuntimeCallInfo *argv) + { + JSThread *thread = argv->GetThread(); + JSHandle valueX = GetCallArg(argv, 0); + JSHandle valueY = GetCallArg(argv, 1); + + if (valueX->IsString() && valueY->IsString()) { + auto xHandle = JSHandle(valueX); + auto yHandle = JSHandle(valueY); + int result = EcmaStringAccessor::Compare(thread->GetEcmaVM(), xHandle, yHandle); + if (result < 0) { + return JSTaggedValue(1); + } + if (result == 0) { + return JSTaggedValue(0); + } + return JSTaggedValue(-1); + } + + if (valueX->IsNumber() && valueY->IsString()) { + return JSTaggedValue(1); + } + if (valueX->IsString() && valueY->IsNumber()) { + return JSTaggedValue(-1); + } + + ComparisonResult res = ComparisonResult::UNDEFINED; + if (valueX->IsNumber() && valueY->IsNumber()) { + res = JSTaggedValue::StrictNumberCompare(valueY->GetNumber(), valueX->GetNumber()); + } else { + res = JSTaggedValue::Compare(thread, valueY, valueX); + } + return res == ComparisonResult::GREAT ? + JSTaggedValue(1) : (res == ComparisonResult::LESS ? JSTaggedValue(-1) : JSTaggedValue(0)); + } + }; + + JSTaggedValue InitializeTreeSetConstructor() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromASCII("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(ContainerTag::TreeSet))); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo); + JSTaggedValue result = ContainersPrivate::Load(objCallInfo); + TestHelper::TearDownFrame(thread, prev); + + return result; + } + + JSHandle CreateJSAPITreeSet(JSTaggedValue compare = JSTaggedValue::Undefined()) + { + JSHandle compareHandle(thread, compare); + JSHandle newTarget(thread, InitializeTreeSetConstructor()); + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(newTarget.GetTaggedValue()); + objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo); + JSTaggedValue result = ContainersTreeSet::TreeSetConstructor(objCallInfo); + TestHelper::TearDownFrame(thread, prev); + JSHandle set(thread, result); + return set; + } + + void JSTreeSetNextTest(const JSHandle& tset) + { + constexpr uint32_t nodeNumber = 8; + auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo1->SetFunction(JSTaggedValue::Undefined()); + callInfo1->SetThis(tset.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo1); + JSHandle iterValues(thread, ContainersTreeSet::Values(callInfo1)); + TestHelper::TearDownFrame(thread, prev1); + EXPECT_TRUE(iterValues->IsJSAPITreeSetIterator()); + { + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(iterValues.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + result.Update(JSAPITreeSetIterator::Next(callInfo)); + TestHelper::TearDownFrame(thread, prev); + bool isDone = false; + for (int i = 0; !isDone; i++) { + EXPECT_EQ(i, JSIterator::IteratorValue(thread, result)->GetInt()); + // next + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(iterValues.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + result.Update(JSAPITreeSetIterator::Next(callInfo)); + TestHelper::TearDownFrame(thread, prev); + isDone = JSIterator::IteratorComplete(thread, result); + EXPECT_EQ(i + 1 == nodeNumber, isDone); + } + } + } + + void JSTreeSetEntriesTest(const JSHandle& tset) + { + auto callInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo2->SetFunction(JSTaggedValue::Undefined()); + callInfo2->SetThis(tset.GetTaggedValue()); + [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, callInfo2); + JSHandle iter(thread, ContainersTreeSet::Entries(callInfo2)); + TestHelper::TearDownFrame(thread, prev2); + EXPECT_TRUE(iter->IsJSAPITreeSetIterator()); + } + + void JSTreeSetForeachTest(JSHandle& tset) + { + constexpr uint32_t nodeNumber = 8; + constexpr uint32_t step = 2; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dset = CreateJSAPITreeSet(); + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func = + factory->NewJSFunction(env, reinterpret_cast(JSTreeSetTestClass::TestForEachFunc)); + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(tset.GetTaggedValue()); + callInfo->SetCallArg(0, func.GetTaggedValue()); + callInfo->SetCallArg(1, dset.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + ContainersTreeSet::ForEach(callInfo); + TestHelper::TearDownFrame(thread, prev); + } + + EXPECT_EQ(dset->GetSize(thread), nodeNumber / step); + EXPECT_EQ(tset->GetSize(thread), nodeNumber / step); + for (int i = 0; i < nodeNumber; i += step) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(dset.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + JSTaggedValue result = ContainersTreeSet::Has(callInfo); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsTrue()); + } + + // test add string + JSTreeSetStringTest(tset, dset); + } + void JSTreeSetStringTest(JSHandle& tset, JSHandle& dset) + { + constexpr uint32_t nodeNumber = 8; + constexpr uint32_t step = 2; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + for (int i = 0; i < nodeNumber; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(tset.GetTaggedValue()); + callInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + JSTaggedValue result = ContainersTreeSet::Add(callInfo); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsTrue()); + EXPECT_EQ(tset->GetSize(thread), nodeNumber / step + i + 1); + } + EXPECT_EQ(tset->GetSize(thread), nodeNumber / step + nodeNumber); + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func = + factory->NewJSFunction(env, reinterpret_cast(JSTreeSetTestClass::TestForEachFunc)); + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(tset.GetTaggedValue()); + callInfo->SetCallArg(0, func.GetTaggedValue()); + callInfo->SetCallArg(1, dset.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + ContainersTreeSet::ForEach(callInfo); + TestHelper::TearDownFrame(thread, prev); + } + EXPECT_EQ(dset->GetSize(thread), nodeNumber + step); + EXPECT_EQ(tset->GetSize(thread), nodeNumber - step); + for (int i = 0; i < nodeNumber; i += step) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(dset.GetTaggedValue()); + callInfo->SetCallArg(0, key.GetTaggedValue()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + JSTaggedValue result = ContainersTreeSet::Has(callInfo); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsTrue()); + } + } + + void JSTreeSetTest(SerializeData* data) + { + Init(); + constexpr uint32_t nodeNumber = 8; + BaseDeserializer deserializer(thread, data); + JSHandle res = deserializer.ReadValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSAPITreeSet fail"; + EXPECT_TRUE(res->IsJSAPITreeSet()) << "[NotJSAPITreeSet] Deserialize JSAPITreeSet fail"; + JSHandle tset = JSHandle::Cast(res); + EXPECT_TRUE(tset->GetSize(thread) == nodeNumber) << "the treeset size Not equal"; + for (int i = 0; i < nodeNumber; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(tset.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + JSTaggedValue result = ContainersTreeSet::Has(callInfo); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsTrue()); + } + + // test values next done + JSTreeSetNextTest(tset); + + // test entries + JSTreeSetEntriesTest(tset); + + // test foreach function with TestForEachFunc; + JSTreeSetForeachTest(tset); + + Destroy(); + } + void JSSharedArrayBufferTest(SerializeData *data, int32_t byteLength, const char *msg) { Init(); @@ -1258,6 +1533,46 @@ public: JSThread *thread {nullptr}; EcmaVM *ecmaVm {nullptr}; EcmaHandleScope *scope {nullptr}; + EcmaVM *instance {nullptr}; + +protected: + JSTaggedValue InitializeTreeSetConstructor() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromASCII("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(ContainerTag::TreeSet))); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo); + JSTaggedValue result = ContainersPrivate::Load(objCallInfo); + TestHelper::TearDownFrame(thread, prev); + + return result; + } + + JSHandle CreateJSAPITreeSet(JSTaggedValue compare = JSTaggedValue::Undefined()) + { + JSHandle compareHandle(thread, compare); + JSHandle newTarget(thread, InitializeTreeSetConstructor()); + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(newTarget.GetTaggedValue()); + objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo); + JSTaggedValue result = ContainersTreeSet::TreeSetConstructor(objCallInfo); + TestHelper::TearDownFrame(thread, prev); + JSHandle set(thread, result); + return set; + } }; HWTEST_F_L0(JSSerializerTest, SerializeJSSpecialValue) @@ -1884,6 +2199,53 @@ HWTEST_F_L0(JSSerializerTest, SerializeJSMap) delete serializer; }; +HWTEST_F_L0(JSSerializerTest, SerializeJSTreeSet) +{ + constexpr int nodeNumber = 8; + JSHandle tset = CreateJSAPITreeSet(); + for (int i = 0; i < nodeNumber; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(tset.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + JSTaggedValue result = ContainersTreeSet::Add(callInfo); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsTrue()); + EXPECT_EQ(tset->GetSize(thread), i + 1); + } + auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo1->SetFunction(JSTaggedValue::Undefined()); + callInfo1->SetThis(tset.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo1); + JSHandle iterValues(thread, ContainersTreeSet::Values(callInfo1)); + TestHelper::TearDownFrame(thread, prev1); + EXPECT_TRUE(iterValues->IsJSAPITreeSetIterator()); + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + bool isDone = false; + for (int i = 0; !isDone; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(iterValues.GetTaggedValue()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); + result.Update(JSAPITreeSetIterator::Next(callInfo)); + TestHelper::TearDownFrame(thread, prev); + isDone = JSIterator::IteratorComplete(thread, result); + EXPECT_EQ(i == nodeNumber, isDone); + } + ValueSerializer *serializer = new ValueSerializer(thread); + bool success = serializer->WriteValue(thread, JSHandle(tset), + JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Undefined())); + EXPECT_TRUE(success) << "Serialize JSTreeSet fail"; + std::unique_ptr data = serializer->Release(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSTreeSetTest, jsDeserializerTest, data.release()); + ecmascript::ThreadSuspensionScope scope(thread); + t1.join(); + delete serializer; +}; + HWTEST_F_L0(JSSerializerTest, SerializeJSRegExp) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); diff --git a/ecmascript/serializer/value_serializer.cpp b/ecmascript/serializer/value_serializer.cpp index a54bbd3888b791bd3a6b8eb2d9b53586b47c0d4f..d8415cf01a77eefb2ba3a04d08a8fd6c85236a3e 100644 --- a/ecmascript/serializer/value_serializer.cpp +++ b/ecmascript/serializer/value_serializer.cpp @@ -57,6 +57,7 @@ bool ValueSerializer::CheckObjectCanSerialize(TaggedObject *object, bool &findSh case JSType::JS_BIGUINT64_ARRAY: case JSType::JS_ARRAY_BUFFER: case JSType::JS_SHARED_ARRAY_BUFFER: + case JSType::JS_API_TREE_SET: case JSType::LINE_STRING: case JSType::TREE_STRING: case JSType::SLICED_STRING: