From 28c9d12162c0ce705870784cdc66dbef55b469f2 Mon Sep 17 00:00:00 2001 From: xliu Date: Wed, 9 Feb 2022 09:36:28 +0800 Subject: [PATCH] Add TreeMap and TreeSet Description To ensure the high performance of container classes, TreeMap and TreeSet is provided in ark. Add test cases for TaggedTree. Related issue #I4PQ1G:add TreeMap and TreeSet Change-Id: I5cda72d06a71380711374109a87e971af6a8c5b7 Signed-off-by: xliu --- BUILD.gn | 7 + ecmascript/builtins.cpp | 8 +- ecmascript/builtins.h | 1 + ecmascript/containers/containers_arraylist.h | 3 + ecmascript/containers/containers_private.cpp | 182 ++- ecmascript/containers/containers_private.h | 11 +- ecmascript/containers/containers_treemap.cpp | 388 ++++++ ecmascript/containers/containers_treemap.h | 57 + ecmascript/containers/containers_treeset.cpp | 328 +++++ ecmascript/containers/containers_treeset.h | 55 + ecmascript/dump.cpp | 267 ++++ ecmascript/global_env_constants.h | 3 + ecmascript/hprof/heap_snapshot.cpp | 8 + ecmascript/interpreter/fast_runtime_stub.h | 4 +- ecmascript/js_api_tree_map.cpp | 106 ++ ecmascript/js_api_tree_map.h | 65 + ecmascript/js_api_tree_map_iterator.cpp | 97 ++ ecmascript/js_api_tree_map_iterator.h | 51 + ecmascript/js_api_tree_set.cpp | 98 ++ ecmascript/js_api_tree_set.h | 60 + ecmascript/js_api_tree_set_iterator.cpp | 92 ++ ecmascript/js_api_tree_set_iterator.h | 51 + ecmascript/js_hclass.h | 41 +- ecmascript/js_tagged_value-inl.h | 20 + ecmascript/js_tagged_value.cpp | 12 + ecmascript/js_tagged_value.h | 8 + ecmascript/mem/c_containers.h | 3 + ecmascript/mem/object_xray-inl.h | 16 + ecmascript/object_factory.cpp | 48 + ecmascript/object_factory.h | 8 + ecmascript/runtime_call_id.h | 37 +- .../snapshot/mem/snapshot_serialize.cpp | 45 + ecmascript/tagged_tree-inl.h | 509 ++++++++ ecmascript/tagged_tree.cpp | 608 +++++++++ ecmascript/tagged_tree.h | 234 ++++ ecmascript/tests/BUILD.gn | 29 + ecmascript/tests/dump_test.cpp | 53 + ecmascript/tests/tagged_tree_test.cpp | 1108 +++++++++++++++++ test/resource/js_runtime/ohos_test.xml | 5 + 39 files changed, 4706 insertions(+), 20 deletions(-) create mode 100644 ecmascript/containers/containers_treemap.cpp create mode 100644 ecmascript/containers/containers_treemap.h create mode 100644 ecmascript/containers/containers_treeset.cpp create mode 100644 ecmascript/containers/containers_treeset.h create mode 100644 ecmascript/js_api_tree_map.cpp create mode 100644 ecmascript/js_api_tree_map.h create mode 100644 ecmascript/js_api_tree_map_iterator.cpp create mode 100644 ecmascript/js_api_tree_map_iterator.h create mode 100644 ecmascript/js_api_tree_set.cpp create mode 100644 ecmascript/js_api_tree_set.h create mode 100644 ecmascript/js_api_tree_set_iterator.cpp create mode 100644 ecmascript/js_api_tree_set_iterator.h create mode 100644 ecmascript/tagged_tree-inl.h create mode 100644 ecmascript/tagged_tree.cpp create mode 100644 ecmascript/tagged_tree.h create mode 100644 ecmascript/tests/tagged_tree_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 6057ce7354..e82b8e9aab 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -291,6 +291,8 @@ ecma_source = [ "ecmascript/class_linker/panda_file_translator.cpp", "ecmascript/containers/containers_arraylist.cpp", "ecmascript/containers/containers_private.cpp", + "ecmascript/containers/containers_treemap.cpp", + "ecmascript/containers/containers_treeset.cpp", "ecmascript/cpu_profiler/cpu_profiler.cpp", "ecmascript/cpu_profiler/profile_processor.cpp", "ecmascript/cpu_profiler/profile_generator.cpp", @@ -324,6 +326,10 @@ ecma_source = [ "ecmascript/interpreter/slow_runtime_helper.cpp", "ecmascript/interpreter/slow_runtime_stub.cpp", "ecmascript/jobs/micro_job_queue.cpp", + "ecmascript/js_api_tree_map.cpp", + "ecmascript/js_api_tree_map_iterator.cpp", + "ecmascript/js_api_tree_set.cpp", + "ecmascript/js_api_tree_set_iterator.cpp", "ecmascript/js_arguments.cpp", "ecmascript/js_array.cpp", "ecmascript/js_array_iterator.cpp", @@ -397,6 +403,7 @@ ecma_source = [ "ecmascript/snapshot/mem/snapshot_serialize.cpp", "ecmascript/stub_module.cpp", "ecmascript/tagged_dictionary.cpp", + "ecmascript/tagged_tree.cpp", "ecmascript/template_string.cpp", "ecmascript/vmstat/caller_stat.cpp", "ecmascript/vmstat/runtime_stat.cpp", diff --git a/ecmascript/builtins.cpp b/ecmascript/builtins.cpp index a4112f316d..cc3178c18d 100644 --- a/ecmascript/builtins.cpp +++ b/ecmascript/builtins.cpp @@ -2770,6 +2770,8 @@ JSHandle Builtins::InitializeArkPrivate(const JSHandle &env { JSHandle arkPrivate = factory_->NewEmptyJSObject(); SetFrozenFunction(env, arkPrivate, "Load", ContainersPrivate::Load, FunctionLength::ZERO); + + // It is used to provide non ECMA standard jsapi containers. SetConstant(arkPrivate, "ArrayList", JSTaggedValue(static_cast(containers::ContainerTag::ArrayList))); SetConstant(arkPrivate, "Queue", JSTaggedValue(static_cast(containers::ContainerTag::Queue))); SetConstant(arkPrivate, "Deque", JSTaggedValue(static_cast(containers::ContainerTag::Deque))); @@ -2781,8 +2783,10 @@ JSHandle Builtins::InitializeArkPrivate(const JSHandle &env SetConstant(arkPrivate, "TreeSet", JSTaggedValue(static_cast(containers::ContainerTag::TreeSet))); SetConstant(arkPrivate, "HashMap", JSTaggedValue(static_cast(containers::ContainerTag::HashMap))); SetConstant(arkPrivate, "HashSet", JSTaggedValue(static_cast(containers::ContainerTag::HashSet))); - SetConstant(arkPrivate, "LightWightMap", JSTaggedValue(static_cast(containers::ContainerTag::LightWightMap))); - SetConstant(arkPrivate, "LightWightSet", JSTaggedValue(static_cast(containers::ContainerTag::LightWightSet))); + SetConstant(arkPrivate, "LightWeightMap", + JSTaggedValue(static_cast(containers::ContainerTag::LightWeightMap))); + SetConstant(arkPrivate, "LightWeightSet", + JSTaggedValue(static_cast(containers::ContainerTag::LightWeightSet))); SetConstant(arkPrivate, "PlainArray", JSTaggedValue(static_cast(containers::ContainerTag::PlainArray))); return arkPrivate; } diff --git a/ecmascript/builtins.h b/ecmascript/builtins.h index ac9411ef24..65913f4804 100644 --- a/ecmascript/builtins.h +++ b/ecmascript/builtins.h @@ -209,6 +209,7 @@ private: void SetGetter(const JSHandle &obj, const JSHandle &key, const JSHandle &getter) const; JSHandle InitializeArkTools(const JSHandle &env) const; + // Using to initialize jsapi container JSHandle InitializeArkPrivate(const JSHandle &env) const; void SetConstantObject(const JSHandle &obj, const char *key, JSHandle &value) const; void SetFrozenFunction(const JSHandle &env, const JSHandle &obj, const char *key, diff --git a/ecmascript/containers/containers_arraylist.h b/ecmascript/containers/containers_arraylist.h index 9a23eb369a..74c5d0ef68 100644 --- a/ecmascript/containers/containers_arraylist.h +++ b/ecmascript/containers/containers_arraylist.h @@ -20,6 +20,9 @@ #include "ecmascript/ecma_runtime_call_info.h" namespace panda::ecmascript::containers { +/** + * High performance container interface in jsapi. + * */ class ContainersArrayList : public base::BuiltinsBase { public: static JSTaggedValue ArrayListConstructor(EcmaRuntimeCallInfo *argv); diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp index 4e736b5c8a..814a144d30 100644 --- a/ecmascript/containers/containers_private.cpp +++ b/ecmascript/containers/containers_private.cpp @@ -16,8 +16,14 @@ #include "ecmascript/containers/containers_private.h" #include "containers_arraylist.h" +#include "containers_treemap.h" +#include "containers_treeset.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_api_tree_map.h" +#include "ecmascript/js_api_tree_map_iterator.h" +#include "ecmascript/js_api_tree_set.h" +#include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_arraylist.h" #include "ecmascript/js_function.h" @@ -51,6 +57,32 @@ JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) } break; } + case ContainerTag::TreeMap: { + JSHandle key(factory->NewFromCanBeCompressString("TreeMapConstructor")); + JSTaggedValue value = + FastRuntimeStub::GetPropertyByName(thread, thisValue.GetTaggedValue(), key.GetTaggedValue()); + if (value != JSTaggedValue::Undefined()) { + res = value; + } else { + JSHandle map = InitializeTreeMap(thread); + SetFrozenConstructor(thread, thisValue, "TreeMapConstructor", map); + res = map.GetTaggedValue(); + } + break; + } + case ContainerTag::TreeSet: { + JSHandle key(factory->NewFromCanBeCompressString("TreeSetConstructor")); + JSTaggedValue value = + FastRuntimeStub::GetPropertyByName(thread, thisValue.GetTaggedValue(), key.GetTaggedValue()); + if (value != JSTaggedValue::Undefined()) { + res = value; + } else { + JSHandle set = InitializeTreeSet(thread); + SetFrozenConstructor(thread, thisValue, "TreeSetConstructor", set); + res = set.GetTaggedValue(); + } + break; + } case ContainerTag::Queue: case ContainerTag::END: break; @@ -73,7 +105,7 @@ JSHandle ContainersPrivate::NewContainerConstructor(JSThread *thread JSFunction::SetFunctionLength(thread, ctor, JSTaggedValue(length)); JSHandle nameString(factory->NewFromCanBeCompressString(name)); JSFunction::SetFunctionName(thread, JSHandle(ctor), nameString, - JSHandle(thread, JSTaggedValue::Undefined())); + globalConst->GetHandledUndefined()); JSHandle constructorKey = globalConst->GetHandledConstructorString(); PropertyDescriptor descriptor1(thread, JSHandle::Cast(ctor), true, false, true); JSObject::DefineOwnProperty(thread, prototype, constructorKey, descriptor1); @@ -97,7 +129,7 @@ void ContainersPrivate::SetFrozenFunction(JSThread *thread, const JSHandle &obj, const char *keyChar, JSHandle &value) { - JSObject::PreventExtensions(thread, obj); + JSObject::PreventExtensions(thread, JSHandle::Cast(value)); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle key(factory->NewFromCanBeCompressString(keyChar)); PropertyDescriptor descriptor(thread, value, false, false, false); @@ -188,4 +220,150 @@ JSHandle ContainersPrivate::InitializeArrayList(JSThread *thread) return arrayListFunction; } + +JSHandle ContainersPrivate::InitializeTreeMap(JSThread *thread) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // TreeMap.prototype + JSHandle mapFuncPrototype = factory->NewEmptyJSObject(); + JSHandle mapFuncPrototypeValue(mapFuncPrototype); + // TreeMap.prototype_or_dynclass + JSHandle mapInstanceDynclass = + factory->NewEcmaDynClass(JSAPITreeMap::SIZE, JSType::JS_API_TREE_MAP, mapFuncPrototypeValue); + // TreeMap() = new Function() + JSHandle mapFunction(NewContainerConstructor( + thread, mapFuncPrototype, ContainersTreeMap::TreeMapConstructor, "TreeMap", FuncLength::ZERO)); + JSHandle(mapFunction)->SetFunctionPrototype(thread, mapInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(mapFuncPrototype), constructorKey, mapFunction); + + // TreeMap.prototype + SetFrozenFunction(thread, mapFuncPrototype, "set", ContainersTreeMap::Set, FuncLength::TWO); + SetFrozenFunction(thread, mapFuncPrototype, "get", ContainersTreeMap::Get, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "remove", ContainersTreeMap::Remove, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "hasKey", ContainersTreeMap::HasKey, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "hasValue", ContainersTreeMap::HasValue, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "getFirstKey", ContainersTreeMap::GetFirstKey, FuncLength::ZERO); + SetFrozenFunction(thread, mapFuncPrototype, "getLastKey", ContainersTreeMap::GetLastKey, FuncLength::ZERO); + SetFrozenFunction(thread, mapFuncPrototype, "setAll", ContainersTreeMap::SetAll, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "clear", ContainersTreeMap::Clear, FuncLength::ZERO); + SetFrozenFunction(thread, mapFuncPrototype, "getLowerKey", ContainersTreeMap::GetLowerKey, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "getHigherKey", ContainersTreeMap::GetHigherKey, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "keys", ContainersTreeMap::Keys, FuncLength::ZERO); + SetFrozenFunction(thread, mapFuncPrototype, "values", ContainersTreeMap::Values, FuncLength::ZERO); + SetFrozenFunction(thread, mapFuncPrototype, "replace", ContainersTreeMap::Replace, FuncLength::TWO); + SetFrozenFunction(thread, mapFuncPrototype, "forEach", ContainersTreeMap::ForEach, FuncLength::ONE); + SetFrozenFunction(thread, mapFuncPrototype, "entries", ContainersTreeMap::Entries, FuncLength::ZERO); + + // @@ToStringTag + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + SetStringTagSymbol(thread, env, mapFuncPrototype, "TreeMap"); + // %TreeMapPrototype% [ @@iterator ] + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle entries(factory->NewFromCanBeCompressString("entries")); + JSHandle entriesFunc = + JSObject::GetMethod(thread, JSHandle::Cast(mapFuncPrototype), entries); + PropertyDescriptor descriptor(thread, entriesFunc, false, false, false); + JSObject::DefineOwnProperty(thread, mapFuncPrototype, iteratorSymbol, descriptor); + // length + JSHandle lengthGetter = + CreateGetter(thread, ContainersTreeMap::GetLength, "length", FuncLength::ZERO); + JSHandle lengthKey(thread, globalConst->GetLengthString()); + SetGetter(thread, mapFuncPrototype, lengthKey, lengthGetter); + + InitializeTreeMapIterator(thread); + return mapFunction; +} + +void ContainersPrivate::InitializeTreeMapIterator(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Iterator.dynclass + JSHandle iteratorDynclass = + factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ITERATOR, env->GetIteratorPrototype()); + + // TreeMapIterator.prototype + JSHandle mapIteratorPrototype(factory->NewJSObject(iteratorDynclass)); + // TreeIterator.prototype.next() + SetFrozenFunction(thread, mapIteratorPrototype, "next", JSAPITreeMapIterator::Next, FuncLength::ZERO); + SetStringTagSymbol(thread, env, mapIteratorPrototype, "TreeMap Iterator"); + auto globalConst = const_cast(thread->GlobalConstants()); + globalConst->SetConstant(ConstantIndex::TREEMAP_ITERATOR_PROTOTYPE_INDEX, mapIteratorPrototype.GetTaggedValue()); +} + +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_dynclass + JSHandle setInstanceDynclass = + factory->NewEcmaDynClass(JSAPITreeSet::SIZE, JSType::JS_API_TREE_SET, setFuncPrototypeValue); + // TreeSet() = new Function() + JSHandle setFunction(NewContainerConstructor( + thread, setFuncPrototype, ContainersTreeSet::TreeSetConstructor, "TreeSet", FuncLength::ZERO)); + JSHandle(setFunction)->SetFunctionPrototype(thread, setInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(setFuncPrototype), constructorKey, setFunction); + + // TreeSet.prototype + SetFrozenFunction(thread, setFuncPrototype, "add", ContainersTreeSet::Add, FuncLength::TWO); + SetFrozenFunction(thread, setFuncPrototype, "remove", ContainersTreeSet::Remove, FuncLength::ONE); + SetFrozenFunction(thread, setFuncPrototype, "has", ContainersTreeSet::Has, FuncLength::ONE); + SetFrozenFunction(thread, setFuncPrototype, "getFirstValue", ContainersTreeSet::GetFirstValue, FuncLength::ZERO); + SetFrozenFunction(thread, setFuncPrototype, "getLastValue", ContainersTreeSet::GetLastValue, FuncLength::ZERO); + SetFrozenFunction(thread, setFuncPrototype, "clear", ContainersTreeSet::Clear, FuncLength::ZERO); + SetFrozenFunction(thread, setFuncPrototype, "getLowerValue", ContainersTreeSet::GetLowerValue, FuncLength::ONE); + SetFrozenFunction(thread, setFuncPrototype, "getHigherValue", ContainersTreeSet::GetHigherValue, FuncLength::ONE); + SetFrozenFunction(thread, setFuncPrototype, "popFirst", ContainersTreeSet::PopFirst, FuncLength::ZERO); + SetFrozenFunction(thread, setFuncPrototype, "popLast", ContainersTreeSet::PopLast, FuncLength::ZERO); + SetFrozenFunction(thread, setFuncPrototype, "isEmpty", ContainersTreeSet::IsEmpty, FuncLength::TWO); + SetFrozenFunction(thread, setFuncPrototype, "values", ContainersTreeSet::Values, FuncLength::ZERO); + SetFrozenFunction(thread, setFuncPrototype, "forEach", ContainersTreeSet::ForEach, FuncLength::ONE); + SetFrozenFunction(thread, setFuncPrototype, "entries", ContainersTreeSet::Entries, FuncLength::ZERO); + + // @@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); + 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.dynclass + JSHandle iteratorDynclass = + factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ITERATOR, env->GetIteratorPrototype()); + + // TreeSetIterator.prototype + JSHandle setIteratorPrototype(factory->NewJSObject(iteratorDynclass)); + // TreeSetIterator.prototype.next() + SetFrozenFunction(thread, setIteratorPrototype, "next", JSAPITreeSetIterator::Next, FuncLength::ZERO); + SetStringTagSymbol(thread, env, setIteratorPrototype, "TreeSet Iterator"); + auto globalConst = const_cast(thread->GlobalConstants()); + globalConst->SetConstant(ConstantIndex::TREESET_ITERATOR_PROTOTYPE_INDEX, setIteratorPrototype.GetTaggedValue()); +} } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_private.h b/ecmascript/containers/containers_private.h index ac83063105..98efb1116e 100644 --- a/ecmascript/containers/containers_private.h +++ b/ecmascript/containers/containers_private.h @@ -32,13 +32,14 @@ enum ContainerTag : uint8_t { TreeSet, HashMap, HashSet, - LightWightMap, - LightWightSet, + LightWeightMap, + LightWeightSet, PlainArray, END }; + // Using Lazy-loading container, including ArrayList, Queue, Stack, Vector, List, LinkedList, Deque, -// TreeMap, TreeSet, HashMap, HashSet, LightWightMap, LightWightSet, PlainArray. +// TreeMap, TreeSet, HashMap, HashSet, LightWeightMap, LightWeightSet, PlainArray. // Use through ArkPrivate.Load([ContainerTag]) in js, ContainTag was declaerd in ArkPrivate like ArkPrivate::ArrayList. class ContainersPrivate : public base::BuiltinsBase { public: @@ -63,6 +64,10 @@ private: static void SetStringTagSymbol(JSThread *thread, const JSHandle &env, const JSHandle &obj, const char *key); static JSHandle InitializeArrayList(JSThread *thread); + static JSHandle InitializeTreeMap(JSThread *thread); + static void InitializeTreeMapIterator(JSThread *thread); + static JSHandle InitializeTreeSet(JSThread *thread); + static void InitializeTreeSetIterator(JSThread *thread); }; } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_treemap.cpp b/ecmascript/containers/containers_treemap.cpp new file mode 100644 index 0000000000..683e13e71c --- /dev/null +++ b/ecmascript/containers/containers_treemap.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2021 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 "containers_treemap.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/internal_call_params.h" +#include "ecmascript/js_api_tree_map.h" +#include "ecmascript/js_api_tree_map_iterator.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_tree-inl.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersTreeMap::TreeMapConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + + // new TreeMap + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Set map’s internal slot with a new empty List. + JSHandle map = JSHandle::Cast(obj); + JSTaggedValue internal = TaggedTreeMap::Create(thread); + map->SetTreeMap(thread, internal); + + // If comparefn was supplied, let compare be comparefn; else let compare be hole. + JSHandle compareFn(GetCallArg(argv, 0)); + if (compareFn->IsUndefined() || compareFn->IsNull()) { + return map.GetTaggedValue(); + } + if (!compareFn->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "comparefn is not Callable", JSTaggedValue::Exception()); + } + + TaggedTreeMap::Cast(internal.GetTaggedObject())->SetCompare(thread, compareFn.GetTaggedValue()); + return map.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeMap::Set(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // get and check this map + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle key = GetCallArg(argv, 0); + JSHandle value = GetCallArg(argv, 1); + + JSHandle map = JSHandle::Cast(self); + JSAPITreeMap::Set(thread, map, key, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return map.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeMap::Get(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Get); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map = JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + return JSAPITreeMap::Get(thread, map, key); +} + +JSTaggedValue ContainersTreeMap::Remove(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Remove); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // get and check this map + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map = JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + return JSAPITreeMap::Delete(thread, map, key); +} + +JSTaggedValue ContainersTreeMap::HasKey(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, HasKey); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle key = GetCallArg(argv, 0); + JSHandle map = JSHandle::Cast(self); + + bool flag = JSAPITreeMap::HasKey(thread, JSHandle::Cast(map), key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return GetTaggedBoolean(flag); +} + +JSTaggedValue ContainersTreeMap::HasValue(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, HasValue); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map = JSHandle::Cast(self); + bool flag = map->HasValue(thread, GetCallArg(argv, 0)); + return GetTaggedBoolean(flag); +} + +JSTaggedValue ContainersTreeMap::GetFirstKey(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetFirstKey); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map = JSHandle::Cast(self); + return TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())->GetFirstKey(); +} + +JSTaggedValue ContainersTreeMap::GetLastKey(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetLastKey); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map = JSHandle::Cast(self); + return TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())->GetLastKey(); +} + +JSTaggedValue ContainersTreeMap::SetAll(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, SetAll); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Incorrect parameters, it should be TreeMap", JSTaggedValue::Exception()); + } + + JSHandle dst = JSHandle::Cast(self); + JSHandle dmap(thread, dst->GetTreeMap()); + JSHandle smap(thread, JSHandle::Cast(obj)->GetTreeMap()); + + JSTaggedValue tmap = TaggedTreeMap::SetAll(thread, dmap, smap); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + dst->SetTreeMap(thread, tmap); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersTreeMap::Clear(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Clear); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSAPITreeMap::Clear(thread, JSHandle::Cast(self)); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersTreeMap::GetLowerKey(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetLowerKey); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map = JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + + JSHandle tmap(thread, map->GetTreeMap()); + return TaggedTreeMap::GetLowerKey(thread, tmap, key); +} + +JSTaggedValue ContainersTreeMap::GetHigherKey(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetHigherKey); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map= JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + + JSHandle tmap(thread, map->GetTreeMap()); + return TaggedTreeMap::GetHigherKey(thread, tmap, key); +} + +JSTaggedValue ContainersTreeMap::Replace(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Replace); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + JSHandle map = JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + JSHandle value = GetCallArg(argv, 1); + + bool success = JSAPITreeMap::Replace(thread, map, key, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return GetTaggedBoolean(success); +} + +JSTaggedValue ContainersTreeMap::Keys(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSAPITreeMapIterator::CreateTreeMapIterator(thread, self, IterationKind::KEY); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeMap::Values(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSAPITreeMapIterator::CreateTreeMapIterator(thread, self, IterationKind::VALUE); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeMap::Entries(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = + JSAPITreeMapIterator::CreateTreeMapIterator(thread, self, IterationKind::KEY_AND_VALUE); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeMap::ForEach(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, ForEach); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check TreeMap object + JSHandle self = GetThis(argv); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + + // get and check callback function + JSHandle func(GetCallArg(argv, 0)); + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The first arg is not Callable", JSTaggedValue::Exception()); + } + + // If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArg = GetCallArg(argv, 1); + JSHandle tmap = JSHandle::Cast(self); + JSMutableHandle iteratedMap(thread, tmap->GetTreeMap()); + int elements = iteratedMap->NumberOfElements(); + JSMutableHandle entries(TaggedTreeMap::GetArrayFromMap(thread, iteratedMap)); + + int index = 0; + int length = entries->GetLength(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + while (index < elements) { + int entriesIndex = entries->Get(index).GetInt(); + key.Update(iteratedMap->GetKey(entriesIndex)); + value.Update(iteratedMap->GetValue(entriesIndex)); + + // Let funcResult be Call(callbackfn, T, «e, e, S»). + arguments->MakeArgv(value, key, self); + JSTaggedValue ret = JSFunction::Call(thread, func, thisArg, 3, arguments->GetArgv()); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); + + // check entries should be update, size will be update in tmap set or remove. + if (tmap->GetSize() != length) { + iteratedMap.Update(tmap->GetTreeMap()); + entries.Update(TaggedTreeMap::GetArrayFromMap(thread, iteratedMap).GetTaggedValue()); + elements = iteratedMap->NumberOfElements(); + length = entries->GetLength(); + } + index++; + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersTreeMap::GetLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, GetLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + int count = JSHandle::Cast(self)->GetSize(); + return JSTaggedValue(count); +} + +JSTaggedValue ContainersTreeMap::IsEmpty(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeMap, IsEmpty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this map + JSHandle self = GetThis(argv); + if (!self->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeMap", JSTaggedValue::Exception()); + } + JSHandle map = JSHandle::Cast(self); + return GetTaggedBoolean(map->GetSize() == 0); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_treemap.h b/ecmascript/containers/containers_treemap.h new file mode 100644 index 0000000000..40afd85661 --- /dev/null +++ b/ecmascript/containers/containers_treemap.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_TREEMAP_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_TREEMAP_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +/** + * High performance container interface in jsapi. + * TreeMap provides ordered maps. + * */ +class ContainersTreeMap : public base::BuiltinsBase { +public: + static JSTaggedValue TreeMapConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue HasKey(EcmaRuntimeCallInfo *argv); + static JSTaggedValue HasValue(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetFirstKey(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLastKey(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Get(EcmaRuntimeCallInfo *argv); + static JSTaggedValue SetAll(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Remove(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetLowerKey(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetHigherKey(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Replace(EcmaRuntimeCallInfo *argv); + static JSTaggedValue IsEmpty(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_TREEMAP_H_ diff --git a/ecmascript/containers/containers_treeset.cpp b/ecmascript/containers/containers_treeset.cpp new file mode 100644 index 0000000000..788623e0cd --- /dev/null +++ b/ecmascript/containers/containers_treeset.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2021 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 "containers_treeset.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/internal_call_params.h" +#include "ecmascript/js_api_tree_set.h" +#include "ecmascript/js_api_tree_set_iterator.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_tree-inl.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersTreeSet::TreeSetConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // new TreeSet + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Set set’s internal slot with a new empty List. + JSHandle set = JSHandle::Cast(obj); + JSTaggedValue internal = TaggedTreeSet::Create(thread); + set->SetTreeSet(thread, internal); + + // If comparefn was supplied, let compare be comparefn; else let compare be hole. + JSHandle compareFn(GetCallArg(argv, 0)); + if (compareFn->IsUndefined() || compareFn->IsNull()) { + return set.GetTaggedValue(); + } + if (!compareFn->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "comparefn is not Callable", JSTaggedValue::Exception()); + } + + TaggedTreeSet::Cast(internal.GetTaggedObject())->SetCompare(thread, compareFn.GetTaggedValue()); + return set.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeSet::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self = GetThis(argv); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSHandle set = JSHandle::Cast(self); + JSAPITreeSet::Add(thread, set, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersTreeSet::Remove(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Remove); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self = GetThis(argv); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle set = JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + return GetTaggedBoolean(JSAPITreeSet::Delete(thread, set, key)); +} + +JSTaggedValue ContainersTreeSet::Has(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle key = GetCallArg(argv, 0); + JSHandle set = JSHandle::Cast(self); + + bool flag = JSAPITreeSet::Has(thread, JSHandle::Cast(set), key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return GetTaggedBoolean(flag); +} + +JSTaggedValue ContainersTreeSet::GetFirstValue(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetFirstValue); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle set = JSHandle::Cast(self); + return TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())->GetFirstKey(); +} + +JSTaggedValue ContainersTreeSet::GetLastValue(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLastValue); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle set = JSHandle::Cast(self); + return TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())->GetLastKey(); +} + +JSTaggedValue ContainersTreeSet::Clear(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Clear); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSAPITreeSet::Clear(thread, JSHandle::Cast(self)); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersTreeSet::GetLowerValue(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLowerValue); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle set = JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + + JSHandle tset(thread, set->GetTreeSet()); + return TaggedTreeSet::GetLowerKey(thread, tset, key); +} + +JSTaggedValue ContainersTreeSet::GetHigherValue(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetHigherValue); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle set = JSHandle::Cast(self); + JSHandle key = GetCallArg(argv, 0); + if (!key->IsString() && !key->IsNumber()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Incorrect key parameters", JSTaggedValue::Exception()); + } + JSHandle tset(thread, set->GetTreeSet()); + return TaggedTreeSet::GetHigherKey(thread, tset, key); +} + +JSTaggedValue ContainersTreeSet::PopFirst(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, PopFirst); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle set = JSHandle::Cast(self); + return JSAPITreeSet::PopFirst(thread, set); +} + +JSTaggedValue ContainersTreeSet::PopLast(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, PopLast); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + JSHandle set = JSHandle::Cast(self); + return JSAPITreeSet::PopLast(thread, set); +} + +JSTaggedValue ContainersTreeSet::IsEmpty(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, IsEmpty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self = GetThis(argv); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + JSHandle set = JSHandle::Cast(self); + return GetTaggedBoolean(set->GetSize() == 0); +} + +JSTaggedValue ContainersTreeSet::Values(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSAPITreeSetIterator::CreateTreeSetIterator(thread, self, IterationKind::KEY); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeSet::Entries(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = + JSAPITreeSetIterator::CreateTreeSetIterator(thread, self, IterationKind::KEY_AND_VALUE); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersTreeSet::ForEach(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, ForEach); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // get and check TreeSet object + JSHandle self = GetThis(argv); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + // get and check callback function + JSHandle func(GetCallArg(argv, 0)); + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The first arg is not Callable", JSTaggedValue::Exception()); + } + + // If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArg = GetCallArg(argv, 1); + JSHandle tset = JSHandle::Cast(self); + JSMutableHandle iteratedSet(thread, tset->GetTreeSet()); + int elements = iteratedSet->NumberOfElements(); + JSMutableHandle entries(TaggedTreeSet::GetArrayFromSet(thread, iteratedSet)); + + int index = 0; + int length = entries->GetLength(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + while (index < elements) { + int entriesIndex = entries->Get(index).GetInt(); + key.Update(iteratedSet->GetKey(entriesIndex)); + + // Let funcResult be Call(callbackfn, T, «e, e, S»). + arguments->MakeArgv(key, key, self); + JSTaggedValue ret = JSFunction::Call(thread, func, thisArg, 3, arguments->GetArgv()); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); + + // check entries should be update, size will be update by set add and remove. + if (tset->GetSize() != length) { + iteratedSet.Update(tset->GetTreeSet()); + entries.Update(TaggedTreeSet::GetArrayFromSet(thread, iteratedSet).GetTaggedValue()); + elements = iteratedSet->NumberOfElements(); + length = entries->GetLength(); + } + index++; + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersTreeSet::GetLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check this set + JSHandle self(GetThis(argv)); + if (!self->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPITreeSet", JSTaggedValue::Exception()); + } + + int count = JSHandle::Cast(self)->GetSize(); + return JSTaggedValue(count); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_treeset.h b/ecmascript/containers/containers_treeset.h new file mode 100644 index 0000000000..2f53d4b814 --- /dev/null +++ b/ecmascript/containers/containers_treeset.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_TREESET_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_TREESET_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +/** + * High performance container interface in jsapi. + * TreeSet provides ordered sets. + * */ +class ContainersTreeSet : public base::BuiltinsBase { +public: + static JSTaggedValue TreeSetConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Remove(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetFirstValue(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLastValue(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetLowerValue(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetHigherValue(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue PopFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue PopLast(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue IsEmpty(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_TREESET_H_ diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index bcdaf7341f..64c34a68a5 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -31,6 +31,10 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_tree_map.h" +#include "ecmascript/js_api_tree_map_iterator.h" +#include "ecmascript/js_api_tree_set.h" +#include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_array.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_arraybuffer.h" @@ -73,6 +77,7 @@ #include "ecmascript/mem/machine_code.h" #include "ecmascript/tagged_array.h" #include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_tree-inl.h" #include "ecmascript/template_map.h" #include "ecmascript/transitions_dictionary.h" @@ -82,6 +87,7 @@ using PendingJob = panda::ecmascript::job::PendingJob; static constexpr uint32_t DUMP_TYPE_OFFSET = 12; static constexpr uint32_t DUMP_PROPERTY_OFFSET = 20; +static constexpr uint32_t DUMP_ELEMENT_OFFSET = 2; CString JSHClass::DumpJSType(JSType type) { @@ -252,6 +258,14 @@ CString JSHClass::DumpJSType(JSType type) return "ClassInfoExtractor"; case JSType::JS_ARRAY_LIST: return "ArrayList"; + case JSType::JS_API_TREE_MAP: + return "TreeMap"; + case JSType::JS_API_TREE_SET: + return "TreeSet"; + case JSType::JS_API_TREEMAP_ITERATOR: + return "TreeMapIterator"; + case JSType::JS_API_TREESET_ITERATOR: + return "TreeSetIterator"; default: { CString ret = "unknown type "; return ret + static_cast(type); @@ -592,6 +606,18 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, std::ostream &os) case JSType::JS_ARRAY_LIST: JSArrayList::Cast(obj)->Dump(thread, os); break; + case JSType::JS_API_TREE_MAP: + JSAPITreeMap::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_API_TREE_SET: + JSAPITreeSet::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_API_TREEMAP_ITERATOR: + JSAPITreeMapIterator::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_API_TREESET_ITERATOR: + JSAPITreeSetIterator::Cast(obj)->Dump(thread, os); + break; default: UNREACHABLE(); break; @@ -999,6 +1025,202 @@ void JSMap::Dump(JSThread *thread, std::ostream &os) const map->Dump(thread, os); } +void JSAPITreeMap::Dump(JSThread *thread, std::ostream &os) const +{ + TaggedTreeMap *map = TaggedTreeMap::Cast(GetTreeMap().GetTaggedObject()); + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSAPITreeMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + TaggedTreeMap *map = TaggedTreeMap::Cast(GetTreeMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSAPITreeMapIterator::Dump(JSThread *thread, std::ostream &os) const +{ + TaggedTreeMap *map = + TaggedTreeMap::Cast(JSAPITreeMap::Cast(GetIteratedMap().GetTaggedObject())->GetTreeMap().GetTaggedObject()); + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSAPITreeMapIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + TaggedTreeMap *map = + TaggedTreeMap::Cast(JSAPITreeMap::Cast(GetIteratedMap().GetTaggedObject())->GetTreeMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + vec.push_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.push_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +template +void DumpTaggedTreeEntry(JSThread *thread, T tree, std::ostream &os, int index, bool isMap = false) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue parent(tree->GetParent(index)); + JSTaggedValue val(tree->GetValue(index)); + JSTaggedValue color(static_cast(tree->GetColor(index))); + JSTaggedValue left = tree->GetLeftChild(index); + JSTaggedValue right = tree->GetRightChild(index); + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[entry] " << index << ": "; + os << "\n"; + if (isMap) { + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << " [key]: {"; + JSTaggedValue key(tree->GetKey(index)); + key.DumpTaggedValue(thread, os); + os << std::right << "};"; + os << "\n"; + } + os << std::left << std::setw(DUMP_TYPE_OFFSET) << " [value]: {"; + val.DumpTaggedValue(thread, os); + os << std::right << "};"; + os << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << " [parent]: {"; + parent.DumpTaggedValue(thread, os); + os << std::right << "};"; + os << "\n"; + os << std::left << std::setw(DUMP_TYPE_OFFSET) << " [color]: {"; + color.DumpTaggedValue(thread, os); + os << std::right << "};"; + os << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << " [left]: {"; + left.DumpTaggedValue(thread, os); + os << std::right << "}; "; + os << std::left << std::setw(DUMP_TYPE_OFFSET) << " [right]: {"; + right.DumpTaggedValue(thread, os); + os << std::right << "};"; + os << "\n"; +} +void TaggedTreeMap::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[Elements]: {"; + JSTaggedValue node = TaggedArray::Get(0); + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[Delete]: {"; + node = TaggedArray::Get(1); + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[Capacity]: {"; + node = TaggedArray::Get(2); // 2 means the three element + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[RootNode]: {"; + node = TaggedArray::Get(3); // 3 means the three element + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int index = 0; index < capacity; index++) { + if (GetKey(index).IsHole()) { + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[entry] " << index << ": "; + GetKey(index).DumpTaggedValue(thread, os); + os << "\n"; + } else { + DumpTaggedTreeEntry(thread, const_cast(this), os, index, true); + } + } +} + +void JSAPITreeSet::Dump(JSThread *thread, std::ostream &os) const +{ + TaggedTreeSet *set = TaggedTreeSet::Cast(GetTreeSet().GetTaggedObject()); + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSAPITreeSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + TaggedTreeSet *set = TaggedTreeSet::Cast(GetTreeSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSAPITreeSetIterator::Dump(JSThread *thread, std::ostream &os) const +{ + TaggedTreeSet *set = + TaggedTreeSet::Cast(JSAPITreeSet::Cast(GetIteratedSet().GetTaggedObject())->GetTreeSet().GetTaggedObject()); + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSAPITreeSetIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + TaggedTreeSet *set = + TaggedTreeSet::Cast(JSAPITreeSet::Cast(GetIteratedSet().GetTaggedObject())->GetTreeSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + vec.push_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.push_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +void TaggedTreeSet::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[Elements]: {"; + JSTaggedValue node = TaggedArray::Get(0); + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[Delete]: {"; + node = TaggedArray::Get(1); + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[Capacity]: {"; + node = TaggedArray::Get(2); // 2 means the three element + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[RootNode]: {"; + node = TaggedArray::Get(3); // 3 means the three element + node.DumpTaggedValue(thread, os); + os << std::right << "}" << "\n"; + + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int index = 0; index < capacity; index++) { + if (GetKey(index).IsHole()) { + os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[entry] " << index << ": "; + GetKey(index).DumpTaggedValue(thread, os); + os << "\n"; + } else { + DumpTaggedTreeEntry(thread, const_cast(this), os, index); + } + } +} + void JSForInIterator::Dump(JSThread *thread, std::ostream &os) const { os << " - Object : "; @@ -2065,6 +2287,18 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, case JSType::JS_ARRAY_LIST: JSArrayList::Cast(obj)->DumpForSnapshot(thread, vec); return; + case JSType::JS_API_TREE_MAP: + JSAPITreeMap::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_API_TREE_SET: + JSAPITreeSet::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_API_TREEMAP_ITERATOR: + JSAPITreeMapIterator::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_API_TREESET_ITERATOR: + JSAPITreeSetIterator::Cast(obj)->DumpForSnapshot(thread, vec); + return; default: break; } @@ -2227,6 +2461,37 @@ void LinkedHashMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, } } +void TaggedTreeMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int index = 0; index < capacity; index++) { + JSTaggedValue key(GetKey(index)); + if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { + JSTaggedValue val = GetValue(index); + CString str; + KeyToStd(str, key); + vec.push_back(std::make_pair(str, val)); + } + } +} + +void TaggedTreeSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int index = 0; index < capacity; index++) { + JSTaggedValue key(GetKey(index)); + if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { + CString str; + KeyToStd(str, key); + vec.push_back(std::make_pair(str, JSTaggedValue::Hole())); + } + } +} + void JSObject::DumpForSnapshot(JSThread *thread, std::vector> &vec) const { DISALLOW_GARBAGE_COLLECTION; @@ -2597,6 +2862,8 @@ void GlobalEnv::DumpForSnapshot([[maybe_unused]] JSThread *thread, vec.push_back(std::make_pair(CString("AsyncFunctionString"), globalConst->GetAsyncFunctionString())); vec.push_back(std::make_pair(CString("ThrowerString"), globalConst->GetThrowerString())); vec.push_back(std::make_pair(CString("Undefined"), globalConst->GetUndefined())); + vec.push_back(std::make_pair(CString("TreeMapIteratorPrototype"), globalConst->GetTreeMapIteratorPrototype())); + vec.push_back(std::make_pair(CString("TreeSetIteratorPrototype"), globalConst->GetTreeSetIteratorPrototype())); } void JSDataView::DumpForSnapshot([[maybe_unused]] JSThread *thread, diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 25515971d2..e1e6bb1f98 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -85,6 +85,9 @@ class JSThread; V(JSTaggedValue, WritableString, WRITABLE_STRING_INDEX, writable) \ V(JSTaggedValue, EnumerableString, ENUMERABLE_STRING_INDEX, enumerable) \ V(JSTaggedValue, ConfigurableString, CONFIGURABLE_STRING_INDEX, configurable) \ + /* non ECMA standard jsapi containers iterators */ \ + V(JSTaggedValue, TreeMapIteratorPrototype, TREEMAP_ITERATOR_PROTOTYPE_INDEX, TreeMapIterator) \ + V(JSTaggedValue, TreeSetIteratorPrototype, TREESET_ITERATOR_PROTOTYPE_INDEX, TreeSetIterator) \ /* SymbolTable*RegisterSymbols */ \ V(JSTaggedValue, NameString, NAME_STRING_INDEX, name) \ V(JSTaggedValue, GetPrototypeOfString, GETPROTOTYPEOF_STRING_INDEX, getPrototypeOf) \ diff --git a/ecmascript/hprof/heap_snapshot.cpp b/ecmascript/hprof/heap_snapshot.cpp index 86f69bc55e..166121906b 100644 --- a/ecmascript/hprof/heap_snapshot.cpp +++ b/ecmascript/hprof/heap_snapshot.cpp @@ -325,6 +325,14 @@ CString *HeapSnapShot::GenerateNodeName(JSThread *thread, TaggedObject *entry) return GetString("EcmaModule"); case JSType::JS_ARRAY_LIST: return GetString("ArrayList"); + case JSType::JS_API_TREE_MAP: + return GetString("TreeMap"); + case JSType::JS_API_TREE_SET: + return GetString("TreeSet"); + case JSType::JS_API_TREEMAP_ITERATOR: + return GetString("TreeMapIterator"); + case JSType::JS_API_TREESET_ITERATOR: + return GetString("TreeSetIterator"); default: break; } diff --git a/ecmascript/interpreter/fast_runtime_stub.h b/ecmascript/interpreter/fast_runtime_stub.h index 5bc8771fc6..077ddb6716 100644 --- a/ecmascript/interpreter/fast_runtime_stub.h +++ b/ecmascript/interpreter/fast_runtime_stub.h @@ -101,7 +101,6 @@ private: friend class ICRuntimeStub; static inline bool IsSpecialIndexedObj(JSType jsType); static inline bool IsSpecialReceiverObj(JSType jsType); - static inline bool IsSpecialContainer(JSType jsType); static inline int32_t TryToElementsIndex(JSTaggedValue key); static inline JSTaggedValue CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue value); @@ -111,6 +110,9 @@ private: PropertyAttributes attr); static inline JSTaggedValue AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value); + + // non ECMA standard jsapi container + static inline bool IsSpecialContainer(JSType jsType); static inline JSTaggedValue GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSType jsType); static inline JSTaggedValue SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, diff --git a/ecmascript/js_api_tree_map.cpp b/ecmascript/js_api_tree_map.cpp new file mode 100644 index 0000000000..1564ca911e --- /dev/null +++ b/ecmascript/js_api_tree_map.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 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/js_api_tree_map.h" + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/tagged_tree-inl.h" + +namespace panda::ecmascript { +void JSAPITreeMap::Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value) +{ + if (!TaggedTreeMap::IsKey(key.GetTaggedValue())) { + THROW_TYPE_ERROR(thread, "the value must be Key of JS"); + } + JSHandle mapHandle(thread, TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())); + + JSTaggedValue newMap = TaggedTreeMap::Set(thread, mapHandle, key, value); + RETURN_IF_ABRUPT_COMPLETION(thread); + map->SetTreeMap(thread, newMap); +} + +JSTaggedValue JSAPITreeMap::Get(JSThread *thread, const JSHandle &map, const JSHandle &key) +{ + JSHandle mapHandle(thread, TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())); + return TaggedTreeMap::Get(thread, mapHandle, key); +} + +int JSAPITreeMap::GetSize() const +{ + return TaggedTreeMap::Cast(GetTreeMap().GetTaggedObject())->NumberOfElements(); +} + +JSTaggedValue JSAPITreeMap::GetKey(int entry) const +{ + ASSERT_PRINT(entry < GetSize(), "entry must less than capacity"); + JSTaggedValue key = TaggedTreeMap::Cast(GetTreeMap().GetTaggedObject())->GetKey(entry); + return key.IsHole() ? JSTaggedValue::Undefined() : key; +} + +JSTaggedValue JSAPITreeMap::GetValue(int entry) const +{ + ASSERT_PRINT(entry < GetSize(), "entry must less than capacity"); + JSTaggedValue value = TaggedTreeMap::Cast(GetTreeMap().GetTaggedObject())->GetValue(entry); + return value.IsHole() ? JSTaggedValue::Undefined() : value; +} + +JSTaggedValue JSAPITreeMap::Delete(JSThread *thread, const JSHandle &map, + const JSHandle &key) +{ + JSHandle mapHandle(thread, TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())); + int entry = TaggedTreeMap::FindEntry(thread, mapHandle, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (entry < 0) { + return JSTaggedValue::Undefined(); + } + JSHandle value(thread, mapHandle->GetValue(entry)); + JSTaggedValue newMap = TaggedTreeMap::Delete(thread, mapHandle, entry); + map->SetTreeMap(thread, newMap); + return value.GetTaggedValue(); +} + +bool JSAPITreeMap::HasKey(JSThread *thread, const JSHandle &map, const JSHandle &key) +{ + JSHandle mapHandle(thread, TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())); + return TaggedTreeMap::FindEntry(thread, mapHandle, key) >= 0; +} + +bool JSAPITreeMap::HasValue(JSThread *thread, const JSHandle &value) const +{ + JSHandle mapHandle(thread, TaggedTreeMap::Cast(GetTreeMap().GetTaggedObject())); + return mapHandle->HasValue(thread, value.GetTaggedValue()); +} + +void JSAPITreeMap::Clear(const JSThread *thread, const JSHandle &map) +{ + int cap = map->GetSize(); + JSTaggedValue internal = TaggedTreeMap::Create(thread, cap); + map->SetTreeMap(thread, internal); +} + +bool JSAPITreeMap::Replace(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value) +{ + JSHandle mapHandle(thread, TaggedTreeMap::Cast(map->GetTreeMap().GetTaggedObject())); + int index = TaggedTreeMap::FindEntry(thread, mapHandle, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (index < 0) { + return false; + } + mapHandle->SetValue(thread, index, value.GetTaggedValue()); + return true; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_tree_map.h b/ecmascript/js_api_tree_map.h new file mode 100644 index 0000000000..e996679d73 --- /dev/null +++ b/ecmascript/js_api_tree_map.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_JS_API_TREE_MAP_H +#define ECMASCRIPT_JS_API_TREE_MAP_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +/** + * Provide the object of non ECMA standard jsapi container. + * JSAPITreeMap provides ordered maps. + * */ +class JSAPITreeMap : public JSObject { +public: + static JSAPITreeMap *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static void Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value); + + static void Clear(const JSThread *thread, const JSHandle &map); + + static JSTaggedValue Get(JSThread *thread, const JSHandle &map, const JSHandle &key); + + static JSTaggedValue Delete(JSThread *thread, const JSHandle &map, + const JSHandle &key); + + static bool HasKey(JSThread *thread, const JSHandle &map, const JSHandle &key); + bool HasValue(JSThread *thread, const JSHandle &value) const; + + static bool Replace(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value); + + int GetSize() const; + + JSTaggedValue GetKey(int entry) const; + + JSTaggedValue GetValue(int entry) const; + + static constexpr size_t TREE_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(TreeMap, TREE_MAP_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TREE_MAP_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_API_TREE_MAP_H diff --git a/ecmascript/js_api_tree_map_iterator.cpp b/ecmascript/js_api_tree_map_iterator.cpp new file mode 100644 index 0000000000..72f51d6709 --- /dev/null +++ b/ecmascript/js_api_tree_map_iterator.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 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 "js_api_tree_map_iterator.h" + +#include "base/builtins_base.h" +#include "js_api_tree_map.h" +#include "js_array.h" +#include "tagged_tree-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSAPITreeMapIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // Let input be the this value + JSHandle input(BuiltinsBase::GetThis(argv)); + + if (!input->IsJSAPITreeMapIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a tree map iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + // Let it be [[IteratedMap]]. + JSHandle iteratedMap(thread, iter->GetIteratedMap()); + + // If it is undefined, return CreateIterResultObject(undefined, true). + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + if (iteratedMap->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, globalConst->GetHandledUndefined(), true).GetTaggedValue(); + } + JSHandle map(thread, JSHandle::Cast(iteratedMap)->GetTreeMap()); + int elements = map->NumberOfElements(); + + JSMutableHandle entries(thread, iter->GetEntries()); + if (elements != static_cast(entries->GetLength())) { + entries.Update(TaggedTreeMap::GetArrayFromMap(thread, map).GetTaggedValue()); + iter->SetEntries(thread, entries); + } + + // Let index be Map.[[NextIndex]]. + int index = iter->GetNextIndex().GetInt(); + if (index < elements) { + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + + int keyIndex = entries->Get(index).GetInt(); + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + + JSHandle key(thread, map->GetKey(keyIndex)); + // If itemKind is key, let result be e.[[Key]] + if (itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, key, false).GetTaggedValue(); + } + JSHandle value(thread, map->GetValue(keyIndex)); + // Else if itemKind is value, let result be e.[[Value]]. + if (itemKind == IterationKind::VALUE) { + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array(factory->NewTaggedArray(2)); // 2 means the length of array + array->Set(thread, 0, key); + array->Set(thread, 1, value); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, array)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); + } + + // Set [[IteratedMap]] to undefined. + iter->SetIteratedMap(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, globalConst->GetHandledUndefined(), true).GetTaggedValue(); +} + +JSHandle JSAPITreeMapIterator::CreateTreeMapIterator(JSThread *thread, + const JSHandle &obj, + IterationKind kind) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSAPITreeMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not TreeMap", thread->GlobalConstants()->GetHandledUndefined()); + } + JSHandle iter(factory->NewJSAPITreeMapIterator(JSHandle(obj), kind)); + return iter; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_tree_map_iterator.h b/ecmascript/js_api_tree_map_iterator.h new file mode 100644 index 0000000000..de6743646f --- /dev/null +++ b/ecmascript/js_api_tree_map_iterator.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_JS_API_TREE_MAP_ITERATOR_H +#define ECMASCRIPT_JS_API_TREE_MAP_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +/** + * It is used to provide iterators for non ECMA standard jsapi containers. + * JSAPITreeMapIterator provides ordered iterators. + * */ +class JSAPITreeMapIterator : public JSObject { +public: + static JSAPITreeMapIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSAPITreeMapIterator()); + return static_cast(obj); + } + static JSHandle CreateTreeMapIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ITERATED_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedMap, ITERATED_MAP_OFFSET, ITERATED_ENTRIES_OFFSET); + ACCESSORS(Entries, ITERATED_ENTRIES_OFFSET, NEXT_INDEX_OFFSET); + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET); + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_MAP_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_API_TREE_MAP_ITERATOR_H diff --git a/ecmascript/js_api_tree_set.cpp b/ecmascript/js_api_tree_set.cpp new file mode 100644 index 0000000000..53e030c7f7 --- /dev/null +++ b/ecmascript/js_api_tree_set.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 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/js_api_tree_set.h" + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/tagged_tree-inl.h" + +namespace panda::ecmascript { +void JSAPITreeSet::Add(JSThread *thread, const JSHandle &set, const JSHandle &value) +{ + if (!TaggedTreeSet::IsKey(value.GetTaggedValue())) { + THROW_TYPE_ERROR(thread, "the value must be Key of JS"); + } + JSHandle setHandle(thread, TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())); + + JSTaggedValue newSet = TaggedTreeSet::Add(thread, setHandle, value); + RETURN_IF_ABRUPT_COMPLETION(thread); + set->SetTreeSet(thread, newSet); +} + +int JSAPITreeSet::GetSize() const +{ + return TaggedTreeSet::Cast(GetTreeSet().GetTaggedObject())->NumberOfElements(); +} + +JSTaggedValue JSAPITreeSet::GetKey(int entry) const +{ + ASSERT_PRINT(entry < GetSize(), "entry must less than capacity"); + JSTaggedValue key = TaggedTreeSet::Cast(GetTreeSet().GetTaggedObject())->GetKey(entry); + return key.IsHole() ? JSTaggedValue::Undefined() : key; +} + +bool JSAPITreeSet::Delete(JSThread *thread, const JSHandle &set, const JSHandle &key) +{ + JSHandle setHandle(thread, TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())); + + int entry = TaggedTreeSet::FindEntry(thread, setHandle, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (entry < 0) { + return false; + } + JSTaggedValue newSet = TaggedTreeSet::Delete(thread, setHandle, entry); + set->SetTreeSet(thread, newSet); + return true; +} + +bool JSAPITreeSet::Has(JSThread *thread, const JSHandle &set, const JSHandle &key) +{ + JSHandle setHandle(thread, TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())); + return TaggedTreeSet::FindEntry(thread, setHandle, key) >= 0; +} + +void JSAPITreeSet::Clear(const JSThread *thread, const JSHandle &set) +{ + int cap = set->GetSize(); + JSTaggedValue internal = TaggedTreeSet::Create(thread, cap); + set->SetTreeSet(thread, internal); +} + +JSTaggedValue JSAPITreeSet::PopFirst(JSThread *thread, const JSHandle &set) +{ + JSHandle setHandle(thread, TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())); + int entry = setHandle->GetMinimum(setHandle->GetRootEntries()); + if (entry < 0) { + return JSTaggedValue::Undefined(); + } + JSHandle value(thread, setHandle->GetKey(entry)); + JSTaggedValue newSet = TaggedTreeSet::Delete(thread, setHandle, entry); + set->SetTreeSet(thread, newSet); + return value.GetTaggedValue(); +} + +JSTaggedValue JSAPITreeSet::PopLast(JSThread *thread, const JSHandle &set) +{ + JSHandle setHandle(thread, TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())); + int entry = setHandle->GetMaximum(setHandle->GetRootEntries()); + if (entry < 0) { + return JSTaggedValue::Undefined(); + } + JSHandle value(thread, setHandle->GetKey(entry)); + JSTaggedValue newSet = TaggedTreeSet::Delete(thread, setHandle, entry); + set->SetTreeSet(thread, newSet); + return value.GetTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_tree_set.h b/ecmascript/js_api_tree_set.h new file mode 100644 index 0000000000..1b19bc44a0 --- /dev/null +++ b/ecmascript/js_api_tree_set.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_JS_API_TREE_SET_H +#define ECMASCRIPT_JS_API_TREE_SET_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +/** + * Provide the object of non ECMA standard jsapi container. + * JSAPITreeSet provides ordered sets. + * */ +class JSAPITreeSet : public JSObject { +public: + static JSAPITreeSet *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static void Add(JSThread *thread, const JSHandle &set, const JSHandle &value); + + static void Clear(const JSThread *thread, const JSHandle &set); + + static bool Delete(JSThread *thread, const JSHandle &set, const JSHandle &key); + + static bool Has(JSThread *thread, const JSHandle &set, const JSHandle &key); + + static JSTaggedValue PopFirst(JSThread *thread, const JSHandle &set); + static JSTaggedValue PopLast(JSThread *thread, const JSHandle &set); + + int GetSize() const; + + JSTaggedValue GetKey(int entry) const; + + JSTaggedValue GetValue(int entry) const; + + static constexpr size_t TREE_SET_OFFSET = JSObject::SIZE; + ACCESSORS(TreeSet, TREE_SET_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TREE_SET_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_API_TREE_SET_H_ diff --git a/ecmascript/js_api_tree_set_iterator.cpp b/ecmascript/js_api_tree_set_iterator.cpp new file mode 100644 index 0000000000..2b137c25ef --- /dev/null +++ b/ecmascript/js_api_tree_set_iterator.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 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 "js_api_tree_set_iterator.h" + +#include "base/builtins_base.h" +#include "js_api_tree_set.h" +#include "js_array.h" +#include "tagged_tree-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSAPITreeSetIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // Let input be the this value + JSHandle input(BuiltinsBase::GetThis(argv)); + + if (!input->IsJSAPITreeSetIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a tree set iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + // Let it be [[IteratedSet]]. + JSHandle iteratedSet(thread, iter->GetIteratedSet()); + + // If it is undefined, return CreateIterResultObject(undefined, true). + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + if (iteratedSet->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, globalConst->GetHandledUndefined(), true).GetTaggedValue(); + } + JSHandle set(thread, JSHandle::Cast(iteratedSet)->GetTreeSet()); + int elements = set->NumberOfElements(); + + JSMutableHandle entries(thread, iter->GetEntries()); + if (elements != static_cast(entries->GetLength())) { + entries.Update(TaggedTreeSet::GetArrayFromSet(thread, set).GetTaggedValue()); + iter->SetEntries(thread, entries); + } + + // Let index be Set.[[NextIndex]]. + int index = iter->GetNextIndex().GetInt(); + if (index < elements) { + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + + int keyIndex = entries->Get(index).GetInt(); + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + + JSHandle key(thread, set->GetKey(keyIndex)); + // If itemKind is key or value, let result be e.[[Key]]. + if (itemKind == IterationKind::VALUE || itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, key, false).GetTaggedValue(); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array(factory->NewTaggedArray(2)); // 2 means the length of array + array->Set(thread, 0, key); + array->Set(thread, 1, key); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, array)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); + } + + // Set [[IteratedSet]] to undefined. + iter->SetIteratedSet(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, globalConst->GetHandledUndefined(), true).GetTaggedValue(); +} + +JSHandle JSAPITreeSetIterator::CreateTreeSetIterator(JSThread *thread, + const JSHandle &obj, + IterationKind kind) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSAPITreeSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not TreeSet", thread->GlobalConstants()->GetHandledUndefined()); + } + JSHandle iter(factory->NewJSAPITreeSetIterator(JSHandle(obj), kind)); + return iter; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_tree_set_iterator.h b/ecmascript/js_api_tree_set_iterator.h new file mode 100644 index 0000000000..db4f6c495e --- /dev/null +++ b/ecmascript/js_api_tree_set_iterator.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_JS_API_TREE_SET_ITERATOR_H +#define ECMASCRIPT_JS_API_TREE_SET_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +/** + * It is used to provide iterators for non ECMA standard jsapi containers. + * JSAPITreeSetIterator provides ordered iterators. + * */ +class JSAPITreeSetIterator : public JSObject { +public: + static JSAPITreeSetIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSAPITreeSetIterator()); + return static_cast(obj); + } + static JSHandle CreateTreeSetIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ITERATED_SET_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedSet, ITERATED_SET_OFFSET, ITERATED_ENTRIES_OFFSET); + ACCESSORS(Entries, ITERATED_ENTRIES_OFFSET, NEXT_INDEX_OFFSET); + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET); + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_SET_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_API_TREE_SET_ITERATOR_H diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 230cb302f4..73cd36660c 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -90,11 +90,13 @@ class ProtoChangeDetails; JS_WEAK_SET, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_DATE, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_ITERATOR, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_FORIN_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_MAP_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_SET_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_ARRAY_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_STRING_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FORIN_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_MAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_TREEMAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_TREESET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ARRAY_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_STRING_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_INTL, /* ///////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_LOCALE, /* /////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_DATE_TIME_FORMAT, /* ///////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -112,8 +114,10 @@ class ProtoChangeDetails; \ /* SPECIAL indexed objects begin, DON'T CHANGE HERE ///////////////////////////////////////////////-PADDING */ \ JS_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_ARRAY_LIST, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_QUEUE, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ARRAY_LIST, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_TREE_MAP, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_TREE_SET, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_QUEUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_TYPED_ARRAY, /* JS_TYPED_ARRAY_BEGIN /////////////////////////////////////////////////////////////////// */ \ JS_INT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_UINT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -598,19 +602,34 @@ public: return GetObjectType() == JSType::JS_PLURAL_RULES; } + // non ECMA standard jsapi containers. + inline bool IsSpecialContainer() const + { + return GetObjectType() >= JSType::JS_ARRAY_LIST && GetObjectType() <= JSType::JS_QUEUE; + } inline bool IsJSArrayList() const { return GetObjectType() == JSType::JS_ARRAY_LIST; } - inline bool IsJSQueue() const { return GetObjectType() == JSType::JS_QUEUE; } - - inline bool IsSpecialContainer() const + inline bool IsJSAPITreeMap() const { - return GetObjectType() >= JSType::JS_ARRAY_LIST && GetObjectType() <= JSType::JS_QUEUE; + return GetObjectType() == JSType::JS_API_TREE_MAP; + } + inline bool IsJSAPITreeSet() const + { + return GetObjectType() == JSType::JS_API_TREE_SET; + } + inline bool IsJSAPITreeMapIterator() const + { + return GetObjectType() == JSType::JS_API_TREEMAP_ITERATOR; + } + inline bool IsJSAPITreeSetIterator() const + { + return GetObjectType() == JSType::JS_API_TREESET_ITERATOR; } inline bool IsAccessorData() const diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index b5897f1339..1012e6a976 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -502,6 +502,16 @@ inline bool JSTaggedValue::IsJSArrayList() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArrayList(); } +inline bool JSTaggedValue::IsJSAPITreeMap() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPITreeMap(); +} + +inline bool JSTaggedValue::IsJSAPITreeSet() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPITreeSet(); +} + inline bool JSTaggedValue::IsSpecialContainer() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsSpecialContainer(); @@ -770,6 +780,16 @@ inline bool JSTaggedValue::IsJSMapIterator() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSMapIterator(); } +inline bool JSTaggedValue::IsJSAPITreeMapIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPITreeMapIterator(); +} + +inline bool JSTaggedValue::IsJSAPITreeSetIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPITreeSetIterator(); +} + inline bool JSTaggedValue::IsJSArrayIterator() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArrayIterator(); diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp index f698b0362f..67ef101673 100644 --- a/ecmascript/js_tagged_value.cpp +++ b/ecmascript/js_tagged_value.cpp @@ -784,6 +784,10 @@ bool JSTaggedValue::HasContainerProperty(JSThread *thread, const JSHandle(obj), key); + } default: { UNREACHABLE(); } @@ -801,6 +805,10 @@ JSHandle JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *threa } case JSType::JS_QUEUE: break; + case JSType::JS_API_TREE_MAP: + case JSType::JS_API_TREE_SET: { + return JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); + } default: { UNREACHABLE(); } @@ -819,6 +827,10 @@ bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle(obj), key, desc); + } default: { UNREACHABLE(); } diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index ae8c1bec2d..ec725d629a 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -310,7 +310,13 @@ public: bool IsJSNumberFormat() const; bool IsJSCollator() const; bool IsJSPluralRules() const; + + // non ECMA standard jsapis bool IsJSArrayList() const; + bool IsJSAPITreeMap() const; + bool IsJSAPITreeSet() const; + bool IsJSAPITreeMapIterator() const; + bool IsJSAPITreeSetIterator() const; bool IsSpecialContainer() const; bool IsPrototypeHandler() const; @@ -343,6 +349,8 @@ private: void DumpSpecialValue([[maybe_unused]] JSThread *thread, std::ostream &os) const; void DumpHeapObjectType([[maybe_unused]] JSThread *thread, std::ostream &os) const; + + // non ECMA standard jsapis static bool HasContainerProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); static JSHandle GetOwnContainerPropertyKeys(JSThread *thread, const JSHandle &obj); diff --git a/ecmascript/mem/c_containers.h b/ecmascript/mem/c_containers.h index 3ce3a6acd3..ecfaa69a79 100644 --- a/ecmascript/mem/c_containers.h +++ b/ecmascript/mem/c_containers.h @@ -53,6 +53,9 @@ using CDeque = std::deque>; template> using CQueue = std::queue; +template> +using CStack= std::stack; + template, class KeyEqual = std::equal_to> using CUnorderedMap = std::unordered_map>>; diff --git a/ecmascript/mem/object_xray-inl.h b/ecmascript/mem/object_xray-inl.h index 6c557efdd8..1b001c93d8 100644 --- a/ecmascript/mem/object_xray-inl.h +++ b/ecmascript/mem/object_xray-inl.h @@ -26,6 +26,10 @@ #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_tree_map.h" +#include "ecmascript/js_api_tree_map_iterator.h" +#include "ecmascript/js_api_tree_set.h" +#include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_arguments.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraylist.h" @@ -299,6 +303,18 @@ void ObjectXRay::VisitObjectBody(TaggedObject *object, JSHClass *klass, const Ec case JSType::JS_ARRAY_LIST: JSArrayList::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::JS_API_TREE_MAP: + JSAPITreeMap::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_TREE_SET: + JSAPITreeSet::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_TREEMAP_ITERATOR: + JSAPITreeMapIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_TREESET_ITERATOR: + JSAPITreeSetIterator::Cast(object)->VisitRangeSlot(visitor); + break; default: UNREACHABLE(); } diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index 00076aaee6..80a1646463 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -36,6 +36,10 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_tree_map.h" +#include "ecmascript/js_api_tree_map_iterator.h" +#include "ecmascript/js_api_tree_set.h" +#include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_arguments.h" #include "ecmascript/js_array.h" #include "ecmascript/js_array_iterator.h" @@ -72,6 +76,7 @@ #include "ecmascript/mem/space.h" #include "ecmascript/record.h" #include "ecmascript/symbol_table-inl.h" +#include "ecmascript/tagged_tree-inl.h" #include "ecmascript/template_map.h" namespace panda::ecmascript { @@ -808,14 +813,23 @@ JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandleSetByteLength(0); JSDataView::Cast(*obj)->SetByteOffset(0); break; + // non ECMA standard jsapi container case JSType::JS_ARRAY_LIST: JSArrayList::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); break; + case JSType::JS_API_TREE_MAP: + JSAPITreeMap::Cast(*obj)->SetTreeMap(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_API_TREE_SET: + JSAPITreeSet::Cast(*obj)->SetTreeSet(thread_, JSTaggedValue::Undefined()); + break; case JSType::JS_FUNCTION: case JSType::JS_GENERATOR_FUNCTION: case JSType::JS_FORIN_ITERATOR: case JSType::JS_MAP_ITERATOR: case JSType::JS_SET_ITERATOR: + case JSType::JS_API_TREEMAP_ITERATOR: + case JSType::JS_API_TREESET_ITERATOR: case JSType::JS_ARRAY_ITERATOR: default: UNREACHABLE(); @@ -2130,4 +2144,38 @@ JSHandle ObjectFactory::ConcatFromString(const JSHandle EcmaString *concatString = EcmaString::Concat(prefix, suffix, vm_); return GetStringFromStringTable(concatString); } + +JSHandle ObjectFactory::NewJSAPITreeMapIterator(const JSHandle &map, + IterationKind kind) +{ + NewObjectHook(); + JSHandle proto(thread_, thread_->GlobalConstants()->GetTreeMapIteratorPrototype()); + JSHandle dynHandle = NewEcmaDynClass(JSAPITreeMapIterator::SIZE, JSType::JS_API_TREEMAP_ITERATOR, proto); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedMap(thread_, map); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + JSHandle tmap(thread_, map->GetTreeMap()); + JSHandle entries = TaggedTreeMap::GetArrayFromMap(thread_, tmap); + iter->SetEntries(thread_, entries); + return iter; +} + +JSHandle ObjectFactory::NewJSAPITreeSetIterator(const JSHandle &set, + IterationKind kind) +{ + NewObjectHook(); + JSHandle proto(thread_, thread_->GlobalConstants()->GetTreeSetIteratorPrototype()); + JSHandle dynHandle = NewEcmaDynClass(JSAPITreeSetIterator::SIZE, JSType::JS_API_TREESET_ITERATOR, proto); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedSet(thread_, set); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + JSHandle tset(thread_, set->GetTreeSet()); + JSHandle entries = TaggedTreeSet::GetArrayFromSet(thread_, tset); + iter->SetEntries(thread_, entries); + return iter; +} } // namespace panda::ecmascript diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index c13019e824..7340ae5b32 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -82,6 +82,10 @@ class LayoutInfo; class JSIntlBoundFunction; class FreeObject; class JSNativePointer; +class JSAPITreeSet; +class JSAPITreeMap; +class JSAPITreeSetIterator; +class JSAPITreeMapIterator; namespace job { class MicroJobQueue; @@ -359,6 +363,10 @@ public: // used for creating jshclass in Builtins, Function, Class_Linker JSHandle NewEcmaDynClass(uint32_t size, JSType type, const JSHandle &prototype); + // It is used to provide iterators for non ECMA standard jsapi containers. + JSHandle NewJSAPITreeMapIterator(const JSHandle &map, IterationKind kind); + JSHandle NewJSAPITreeSetIterator(const JSHandle &set, IterationKind kind); + private: friend class GlobalEnv; friend class GlobalEnvConstants; diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h index a8bfcb2b18..dd9be7c249 100644 --- a/ecmascript/runtime_call_id.h +++ b/ecmascript/runtime_call_id.h @@ -519,7 +519,42 @@ namespace panda::ecmascript { V(WeakSet, Has) \ V(ArrayList, Constructor) \ V(ArrayList, Add) \ - V(ArrayList, Iterator) + V(ArrayList, Iterator) \ + V(TreeMap, Constructor) \ + V(TreeMap, HasKey) \ + V(TreeMap, HasValue) \ + V(TreeMap, GetFirstKey) \ + V(TreeMap, GetLastKey) \ + V(TreeMap, Set) \ + V(TreeMap, Get) \ + V(TreeMap, SetAll) \ + V(TreeMap, Remove) \ + V(TreeMap, Clear) \ + V(TreeMap, GetLowerKey) \ + V(TreeMap, GetHigherKey) \ + V(TreeMap, Replace) \ + V(TreeMap, IsEmpty) \ + V(TreeMap, GetLength) \ + V(TreeMap, Keys) \ + V(TreeMap, Values) \ + V(TreeMap, Entries) \ + V(TreeMap, ForEach) \ + V(TreeSet, Constructor) \ + V(TreeSet, Add) \ + V(TreeSet, Remove) \ + V(TreeSet, Clear) \ + V(TreeSet, Has) \ + V(TreeSet, GetFirstValue) \ + V(TreeSet, GetLastValue) \ + V(TreeSet, GetLowerValue) \ + V(TreeSet, GetHigherValue) \ + V(TreeSet, PopFirst) \ + V(TreeSet, PopLast) \ + V(TreeSet, IsEmpty) \ + V(TreeSet, GetLength) \ + V(TreeSet, Values) \ + V(TreeSet, ForEach) \ + V(TreeSet, Entries) #define ABSTRACT_OPERATION_LIST(V) \ V(JSTaggedValue, ToString) \ diff --git a/ecmascript/snapshot/mem/snapshot_serialize.cpp b/ecmascript/snapshot/mem/snapshot_serialize.cpp index 2bc9b73ad0..54f08a6804 100644 --- a/ecmascript/snapshot/mem/snapshot_serialize.cpp +++ b/ecmascript/snapshot/mem/snapshot_serialize.cpp @@ -54,7 +54,11 @@ #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/class_linker/program_object.h" #include "ecmascript/containers/containers_arraylist.h" +#include "ecmascript/containers/containers_treemap.h" +#include "ecmascript/containers/containers_treeset.h" #include "ecmascript/global_env.h" +#include "ecmascript/js_api_tree_map_iterator.h" +#include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_for_in_iterator.h" #include "ecmascript/js_hclass.h" @@ -112,6 +116,8 @@ using RelativeTimeFormat = builtins::BuiltinsRelativeTimeFormat; using Collator = builtins::BuiltinsCollator; using PluralRules = builtins::BuiltinsPluralRules; using ArrayList = containers::ContainersArrayList; +using TreeMap = containers::ContainersTreeMap; +using TreeSet = containers::ContainersTreeSet; constexpr int TAGGED_SIZE = JSTaggedValue::TaggedTypeSize(); constexpr int OBJECT_HEADER_SIZE = TaggedObject::TaggedObjectSize(); @@ -546,9 +552,48 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(PluralRules::SupportedLocalesOf), reinterpret_cast(PluralRules::Select), reinterpret_cast(PluralRules::ResolvedOptions), + + // non ECMA standard jsapi containers. reinterpret_cast(ArrayList::ArrayListConstructor), reinterpret_cast(ArrayList::Add), reinterpret_cast(ArrayList::Iterator), + reinterpret_cast(TreeMap::TreeMapConstructor), + reinterpret_cast(TreeMap::Set), + reinterpret_cast(TreeMap::Get), + reinterpret_cast(TreeMap::Remove), + reinterpret_cast(TreeMap::GetFirstKey), + reinterpret_cast(TreeMap::GetLastKey), + reinterpret_cast(TreeMap::GetLowerKey), + reinterpret_cast(TreeMap::GetHigherKey), + reinterpret_cast(TreeMap::HasKey), + reinterpret_cast(TreeMap::HasValue), + reinterpret_cast(TreeMap::SetAll), + reinterpret_cast(TreeMap::Replace), + reinterpret_cast(TreeMap::Keys), + reinterpret_cast(TreeMap::Values), + reinterpret_cast(TreeMap::Entries), + reinterpret_cast(TreeMap::ForEach), + reinterpret_cast(TreeMap::Clear), + reinterpret_cast(TreeMap::IsEmpty), + reinterpret_cast(TreeMap::GetLength), + reinterpret_cast(TreeSet::TreeSetConstructor), + reinterpret_cast(TreeSet::Add), + reinterpret_cast(TreeSet::Has), + reinterpret_cast(TreeSet::Remove), + reinterpret_cast(TreeSet::GetFirstValue), + reinterpret_cast(TreeSet::GetLastValue), + reinterpret_cast(TreeSet::GetLowerValue), + reinterpret_cast(TreeSet::GetHigherValue), + reinterpret_cast(TreeSet::PopFirst), + reinterpret_cast(TreeSet::PopLast), + reinterpret_cast(TreeSet::IsEmpty), + reinterpret_cast(TreeSet::Values), + reinterpret_cast(TreeSet::Entries), + reinterpret_cast(TreeSet::ForEach), + reinterpret_cast(TreeSet::Clear), + reinterpret_cast(TreeSet::GetLength), + reinterpret_cast(JSAPITreeMapIterator::Next), + reinterpret_cast(JSAPITreeSetIterator::Next), // not builtins method reinterpret_cast(JSFunction::PrototypeSetter), diff --git a/ecmascript/tagged_tree-inl.h b/ecmascript/tagged_tree-inl.h new file mode 100644 index 0000000000..f8f6cae865 --- /dev/null +++ b/ecmascript/tagged_tree-inl.h @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_TAGGED_TREE_INL_H +#define ECMASCRIPT_TAGGED_TREE_INL_H + +#include "ecmascript/tagged_tree.h" + +#include "ecmascript/global_env.h" +#include "ecmascript/internal_call_params.h" +#include "tagged_array-inl.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript { +// TaggedTree +template +JSTaggedValue TaggedTree::GetRootKey() const +{ + return GetKey(GetRootEntries()); +} + +template +void TaggedTree::SetRoot(JSThread *thread, int index, JSTaggedValue key, JSTaggedValue value) +{ + SetKey(thread, 0, key); + SetValue(thread, 0, value); + SetParent(thread, 0, JSTaggedValue(-1)); + SetColor(thread, 0, TreeColor::BLACK); + SetRootEntries(thread, index); +} + +template +void TaggedTree::SetKey(const JSThread *thread, uint32_t entry, JSTaggedValue key) +{ + uint32_t index = EntryToIndex(entry); + SetElement(thread, index, key); +} + +template +void TaggedTree::SetValue(const JSThread *thread, uint32_t entry, JSTaggedValue value) +{ + uint32_t index = EntryToIndex(entry) + Derived::ENTRY_VALUE_INDEX; + SetElement(thread, index, value); +} + +template +void TaggedTree::SetColor(const JSThread *thread, int entry, TreeColor color) +{ + if (entry >= 0) { + uint32_t index = EntryToIndex(entry) + Derived::ENTRY_COLOR_INDEX; + SetElement(thread, index, JSTaggedValue(static_cast(color))); + } +} + +template +void TaggedTree::SetParent(const JSThread *thread, int entry, JSTaggedValue value) +{ + if (entry < 0) { + return; + } + uint32_t index = EntryToIndex(entry) + Derived::ENTRY_PARENT_INDEX; + SetElement(thread, index, value); +} + +template +void TaggedTree::SetLeftChild(const JSThread *thread, uint32_t entry, JSTaggedValue value) +{ + uint32_t index = EntryToIndex(entry) + Derived::ENTRY_LEFT_CHILD_INDEX; + SetElement(thread, index, value); +} + +template +void TaggedTree::SetRightChild(const JSThread *thread, uint32_t entry, JSTaggedValue value) +{ + uint32_t index = EntryToIndex(entry) + Derived::ENTRY_RIGHT_CHILD_INDEX; + SetElement(thread, index, value); +} + +template +void TaggedTree::SetCompare(const JSThread *thread, JSTaggedValue fn) +{ + Set(thread, COMPARE_FUNCTION_INDEX, fn); +} + +template +JSTaggedValue TaggedTree::GetCompare() const +{ + return Get(COMPARE_FUNCTION_INDEX); +} + +template +JSTaggedValue TaggedTree::GetKey(int entry) const +{ + if (entry < 0) { + return JSTaggedValue::Hole(); + } + int index = EntryToIndex(entry); + return GetElement(index); +} + +template +JSTaggedValue TaggedTree::GetValue(int entry) const +{ + int index = EntryToIndex(entry) + Derived::ENTRY_VALUE_INDEX; + return GetElement(index); +} + +template +TreeColor TaggedTree::GetColor(int entry) const +{ + if (entry < 0) { + return TreeColor::BLACK; + } + int index = EntryToIndex(entry) + Derived::ENTRY_COLOR_INDEX; + JSTaggedValue color = GetElement(index); + return color.GetInt() == TreeColor::RED ? TreeColor::RED : TreeColor::BLACK; +} + +template +uint32_t TaggedTree::EntryToIndex(uint32_t entry) const +{ + return ELEMENTS_START_INDEX + entry * (Derived::ENTRY_SIZE); +} + +template +void TaggedTree::SetElement(const JSThread *thread, uint32_t index, JSTaggedValue element) +{ + ASSERT(index >= 0 && index < GetLength()); + Set(thread, index, element); +} + +template +JSTaggedValue TaggedTree::GetElement(int index) const +{ + ASSERT(index >= 0 && index < static_cast(GetLength())); + return Get(index); +} + +template +int TaggedTree::NumberOfElements() const +{ + return Get(NUMBER_OF_ELEMENTS_INDEX).GetInt(); +} + +template +int TaggedTree::NumberOfDeletedElements() const +{ + return Get(NUMBER_OF_HOLE_ENTRIES_INDEX).GetInt(); +} + +template +void TaggedTree::SetNumberOfElements(const JSThread *thread, int num) +{ + Set(thread, NUMBER_OF_ELEMENTS_INDEX, JSTaggedValue(num)); +} + +template +void TaggedTree::SetNumberOfDeletedElements(const JSThread *thread, int num) +{ + Set(thread, NUMBER_OF_HOLE_ENTRIES_INDEX, JSTaggedValue(num)); +} + +template +void TaggedTree::SetRootEntries(const JSThread *thread, int num) +{ + Set(thread, ROOT_INDEX, JSTaggedValue(num)); +} + +template +int TaggedTree::GetRootEntries() const +{ + return Get(ROOT_INDEX).GetInt(); +} + +template +JSTaggedValue TaggedTree::GetLeftChild(int parent) const +{ + if (parent < 0) { + return JSTaggedValue::Hole(); + } + int index = EntryToIndex(parent) + Derived::ENTRY_LEFT_CHILD_INDEX; + return Get(index); +} + +template +JSTaggedValue TaggedTree::GetRightChild(int parent) const +{ + if (parent < 0) { + return JSTaggedValue::Hole(); + } + int index = EntryToIndex(parent) + Derived::ENTRY_RIGHT_CHILD_INDEX; + return Get(index); +} + +template +int TaggedTree::GetLeftChildIndex(int parent) const +{ + if (parent < 0) { + return -1; + } + int index = EntryToIndex(parent) + Derived::ENTRY_LEFT_CHILD_INDEX; + JSTaggedValue child = Get(index); + return child.IsHole() ? -1 : child.GetInt(); +} + +template +int TaggedTree::GetRightChildIndex(int parent) const +{ + if (parent < 0) { + return -1; + } + int index = EntryToIndex(parent) + Derived::ENTRY_RIGHT_CHILD_INDEX; + JSTaggedValue child = Get(index); + return child.IsHole() ? -1: child.GetInt(); +} + +template +int TaggedTree::GetParent(int entry) const +{ + int index = EntryToIndex(entry) + Derived::ENTRY_PARENT_INDEX; + JSTaggedValue parent = GetElement(index); + return parent.GetInt(); +} + +template +int TaggedTree::GetMinimum(int entry) const +{ + JSTaggedValue child = GetLeftChild(entry); + while (!child.IsHole()) { + entry = child.GetInt(); + child = GetLeftChild(entry); + } + return entry; +} + +template +int TaggedTree::GetMaximum(int entry) const +{ + JSTaggedValue child = GetRightChild(entry); + while (!child.IsHole()) { + entry = child.GetInt(); + child = GetRightChild(entry); + } + return entry; +} + +template +bool TaggedTree::IsLeft(int entry) const +{ + JSTaggedValue child = GetLeftChild(GetParent(entry)); + return child.IsHole() ? false : (child.GetInt() == entry); +} + +template +bool TaggedTree::IsRight(int entry) const +{ + JSTaggedValue child = GetRightChild(GetParent(entry)); + return child.IsHole() ? false : (child.GetInt() == entry); +} + +template +bool TaggedTree::IsVaildIndex(int entry) const +{ + return entry != GetRootEntries() && !GetKey(entry).IsHole(); +} + +template +int TaggedTree::GetLeftBrother(int entry) const +{ + JSTaggedValue child = GetRightChild(GetParent(entry)); + return child.IsHole() ? -1 : child.GetInt(); +} + +template +int TaggedTree::GetRightBrother(int entry) const +{ + JSTaggedValue child = GetLeftChild(GetParent(entry)); + return child.IsHole() ? -1 : child.GetInt(); +} + +template +void TaggedTree::SetCapacity(const JSThread *thread, int capacity) +{ + Set(thread, CAPACITY_INDEX, JSTaggedValue(capacity)); +} + +template +int TaggedTree::Capacity() const +{ + return Get(CAPACITY_INDEX).GetInt(); +} + +template +int TaggedTree::ComputeCapacity(int oldCapacity) +{ + int capacity = (oldCapacity << 1) + 1; + return (capacity > MIN_CAPACITY) ? capacity : MIN_CAPACITY; +} + +template +void TaggedTree::InsertLeftEntry(const JSThread *thread, uint32_t parentIndex, uint32_t entry, + JSTaggedValue key, JSTaggedValue value) +{ + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetColor(thread, entry, TreeColor::RED); + SetParent(thread, entry, JSTaggedValue(parentIndex)); + SetLeftChild(thread, parentIndex, JSTaggedValue(entry)); +} + +template +void TaggedTree::InsertRightEntry(const JSThread *thread, uint32_t parentIndex, uint32_t entry, + JSTaggedValue key, JSTaggedValue value) +{ + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetColor(thread, entry, TreeColor::RED); + SetParent(thread, entry, JSTaggedValue(parentIndex)); + SetRightChild(thread, parentIndex, JSTaggedValue(entry)); +} + +template +void TaggedTree::CopyEntry(const JSThread *thread, int parent, const JSHandle &newTree, int index) +{ + newTree->SetKey(thread, index, GetKey(parent)); + newTree->SetColor(thread, index, GetColor(parent)); +} + +template +void TaggedTree::CopyData(const JSThread *thread, int dst, int src) +{ + SetKey(thread, dst, GetKey(src)); +} + +template +void TaggedTree::CopyAllData(const JSThread *thread, int parent, const JSHandle &newTree, int index) +{ + newTree->SetKey(thread, index, GetKey(parent)); + newTree->SetValue(thread, index, GetValue(parent)); + newTree->SetColor(thread, index, GetColor(parent)); + newTree->SetParent(thread, index, JSTaggedValue(GetParent(parent))); + newTree->SetRightChild(thread, index, GetRightChild(parent)); + newTree->SetLeftChild(thread, index, GetLeftChild(parent)); +} + +template +void TaggedTree::RemoveEntry(const JSThread *thread, int index) +{ + SetKey(thread, index, JSTaggedValue::Hole()); + SetParent(thread, index, JSTaggedValue::Hole()); + SetLeftChild(thread, index, JSTaggedValue::Hole()); + SetRightChild(thread, index, JSTaggedValue::Hole()); +} + +template +ComparisonResult TaggedTree::EntryCompare(JSThread *thread, const JSHandle valueX, + const JSHandle valueY, JSHandle tree) +{ + JSTaggedValue fn = tree->GetCompare(); + if (fn.IsHole()) { + return OrdinayEntryCompare(thread, valueX, valueY); + } + + JSHandle compareFn(thread, fn); + JSHandle thisArgHandle = thread->GlobalConstants()->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(valueX, valueY); + JSTaggedValue callResult = + JSFunction::Call(thread, compareFn, thisArgHandle, 2, arguments->GetArgv()); // 2: two args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + int compareResult = 0; + if (callResult.IsInt()) { + compareResult = callResult.GetInt(); + } else { + JSHandle resultHandle(thread, callResult); + JSTaggedNumber v = JSTaggedValue::ToNumber(thread, resultHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + double value = v.GetNumber(); + if (std::isnan(value)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "CompareFn has illegal return value", ComparisonResult::UNDEFINED); + } + compareResult = static_cast(value); + } + return compareResult > 0 ? ComparisonResult::GREAT : + (compareResult < 0 ? ComparisonResult::LESS : ComparisonResult::EQUAL); +} + +template +ComparisonResult TaggedTree::OrdinayEntryCompare(JSThread *thread, const JSHandle valueX, + const JSHandle valueY) +{ + if (valueX->IsString() && valueY->IsString()) { + auto xString = static_cast(valueX->GetTaggedObject()); + auto yString = static_cast(valueY->GetTaggedObject()); + int result = xString->Compare(yString); + if (result < 0) { + return ComparisonResult::LESS; + } + if (result == 0) { + return ComparisonResult::EQUAL; + } + return ComparisonResult::GREAT; + } + + if (valueX->IsNumber() && valueY->IsNumber()) { + return JSTaggedValue::StrictNumberCompare(valueX->GetNumber(), valueY->GetNumber()); + } + + if (valueX->IsNumber() && valueY->IsString()) { + return ComparisonResult::LESS; + } + if (valueX->IsString() && valueY->IsNumber()) { + return ComparisonResult::GREAT; + } + + JSHandle xValueHandle(JSTaggedValue::ToString(thread, valueX)); + JSHandle yValueHandle(JSTaggedValue::ToString(thread, valueY)); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Compare(thread, xValueHandle, yValueHandle); +} + +template +JSTaggedValue TaggedTree::Transform(JSTaggedValue v) const +{ + return v.IsHole() ? JSTaggedValue::Undefined() : v; +} + +// TaggedTreeMap +void TaggedTreeMap::CopyEntry(const JSThread *thread, int parent, const JSHandle &newMap, int index) +{ + RBTree::CopyEntry(thread, parent, newMap, index); + newMap->SetValue(thread, index, GetValue(parent)); +} + +void TaggedTreeMap::CopyData(const JSThread *thread, int dst, int src) +{ + RBTree::CopyData(thread, dst, src); + SetValue(thread, dst, GetValue(src)); +} + +void TaggedTreeMap::RemoveEntry(const JSThread *thread, int index) +{ + RBTree::RemoveEntry(thread, index); + SetValue(thread, index, JSTaggedValue::Hole()); +} + +JSTaggedValue TaggedTreeMap::Get(JSThread *thread, const JSHandle &map, + const JSHandle &key) +{ + int index = RBTree::FindEntry(thread, map, key); + return index == -1 ? JSTaggedValue::Undefined() : map->GetValue(index); +} + +JSTaggedValue TaggedTreeMap::GetFirstKey() const +{ + JSTaggedValue key = GetKey(GetMinimum(GetRootEntries())); + return Transform(key); +} + +JSTaggedValue TaggedTreeMap::GetLastKey() const +{ + JSTaggedValue key = GetKey(GetMaximum(GetRootEntries())); + return Transform(key); +} + +// TaggedTreeSet +void TaggedTreeSet::CopyEntry(const JSThread *thread, int parent, const JSHandle &newMap, int index) +{ + RBTree::CopyEntry(thread, parent, newMap, index); + newMap->SetValue(thread, index, GetValue(parent)); +} + +void TaggedTreeSet::CopyData(const JSThread *thread, int dst, int src) +{ + RBTree::CopyData(thread, dst, src); + SetValue(thread, dst, GetValue(src)); +} + +void TaggedTreeSet::RemoveEntry(const JSThread *thread, int index) +{ + RBTree::RemoveEntry(thread, index); + SetValue(thread, index, JSTaggedValue::Hole()); +} + +JSTaggedValue TaggedTreeSet::GetFirstKey() const +{ + JSTaggedValue key = GetKey(GetMinimum(GetRootEntries())); + return Transform(key); +} + +JSTaggedValue TaggedTreeSet::GetLastKey() const +{ + JSTaggedValue key = GetKey(GetMaximum(GetRootEntries())); + return Transform(key); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_TREE_INL_H diff --git a/ecmascript/tagged_tree.cpp b/ecmascript/tagged_tree.cpp new file mode 100644 index 0000000000..52e487da47 --- /dev/null +++ b/ecmascript/tagged_tree.cpp @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2021 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/tagged_tree-inl.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/object_factory.h" +#include "libpandabase/utils/bit_utils.h" + +namespace panda::ecmascript { +template +JSHandle TaggedTree::Create(const JSThread *thread, int numberOfElements) +{ + ASSERT_PRINT(numberOfElements > 0, "size must be a non-negative integer"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto capacity = numberOfElements > MIN_CAPACITY ? static_cast(numberOfElements) : MIN_CAPACITY; + int length = ELEMENTS_START_INDEX + numberOfElements * (Derived::ENTRY_SIZE); + + auto tree = JSHandle::Cast(factory->NewTaggedArray(length)); + tree->SetNumberOfElements(thread, 0); + tree->SetNumberOfDeletedElements(thread, 0); + tree->SetRootEntries(thread, -1); + tree->SetCompare(thread, JSTaggedValue::Hole()); + tree->SetCapacity(thread, capacity); + return tree; +} + +template +void TaggedTree::InsertRebalance(const JSThread *thread, int index) +{ + while (IsVaildIndex(index) && GetColor(GetParent(index)) == TreeColor::RED) { + if (IsLeft(GetParent(index))) { + int bro = GetLeftBrother(GetParent(index)); + if (GetColor(bro)) { + SetColor(thread, GetParent(index), TreeColor::BLACK); + SetColor(thread, bro, TreeColor::BLACK); + SetColor(thread, GetParent(GetParent(index)), TreeColor::RED); + index = GetParent(GetParent(index)); + } else { + if (IsRight(index)) { + index = GetParent(index); + LeftRotate(thread, index); + } + SetColor(thread, GetParent(index), TreeColor::BLACK); + SetColor(thread, GetParent(GetParent(index)), TreeColor::RED); + RightRotate(thread, GetParent(GetParent(index))); + } + } else { + int bro = GetRightBrother(GetParent(index)); + if (GetColor(bro)) { + SetColor(thread, GetParent(index), TreeColor::BLACK); + SetColor(thread, bro, TreeColor::BLACK); + SetColor(thread, GetParent(GetParent(index)), TreeColor::RED); + index = GetParent(GetParent(index)); + } else { + if (IsLeft(index)) { + index = GetParent(index); + RightRotate(thread, index); + } + SetColor(thread, GetParent(index), TreeColor::BLACK); + SetColor(thread, GetParent(GetParent(index)), TreeColor::RED); + LeftRotate(thread, GetParent(GetParent(index))); + } + } + } + SetColor(thread, GetRootEntries(), TreeColor::BLACK); +} + +template +void TaggedTree::LeftRotate(const JSThread *thread, int index) +{ + if (index >= 0) { + int right = GetRightChild(index).GetInt(); + JSTaggedValue leftOfRight = GetLeftChild(right); + SetRightChild(thread, index, leftOfRight); + if (!leftOfRight.IsHole()) { + SetParent(thread, leftOfRight.GetInt(), JSTaggedValue(index)); + } + int parentOfIndex = GetParent(index); + SetParent(thread, right, JSTaggedValue(parentOfIndex)); + if (parentOfIndex < 0) { + SetRootEntries(thread, right); + } else { + JSTaggedValue left = GetLeftChild(parentOfIndex); + if (!left.IsHole() && left.GetInt() == index) { // change to isleft + SetLeftChild(thread, parentOfIndex, JSTaggedValue(right)); + } else { + SetRightChild(thread, parentOfIndex, JSTaggedValue(right)); + } + } + SetLeftChild(thread, right, JSTaggedValue(index)); + SetParent(thread, index, JSTaggedValue(right)); + } +} + +template +void TaggedTree::RightRotate(const JSThread *thread, int index) +{ + if (index >= 0) { + int left = GetLeftChild(index).GetInt(); + JSTaggedValue rightOfLeft = GetRightChild(left); + SetLeftChild(thread, index, rightOfLeft); + if (!rightOfLeft.IsHole()) { + SetParent(thread, rightOfLeft.GetInt(), JSTaggedValue(index)); + } + int parentOfIndex = GetParent(index); + SetParent(thread, left, JSTaggedValue(parentOfIndex)); + if (parentOfIndex < 0) { + SetRootEntries(thread, left); + } else { + JSTaggedValue right = GetRightChild(parentOfIndex); + if (!right.IsHole() && right.GetInt() == index) { // change to isright + SetRightChild(thread, parentOfIndex, JSTaggedValue(left)); + } else { + SetLeftChild(thread, parentOfIndex, JSTaggedValue(left)); + } + } + SetRightChild(thread, left, JSTaggedValue(index)); + SetParent(thread, index, JSTaggedValue(left)); + } +} + +template +JSHandle TaggedTree::AdjustTaggedTree(const JSThread *thread, const JSHandle &tree, int len) +{ + JSMutableHandle newTree(thread, JSTaggedValue::Undefined()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (tree->NumberOfDeletedElements() == 0) { + newTree.Update(factory->ExtendArray(JSHandle::Cast(tree), len).GetTaggedValue()); + return newTree; + } + + int elements = tree->NumberOfElements(); + newTree.Update(factory->NewTaggedArray(len).GetTaggedValue()); + newTree->SetNumberOfElements(thread, elements); + newTree->SetNumberOfDeletedElements(thread, 0); + + newTree->SetRootEntries(thread, 0); + CQueue entries; + entries.push(tree->GetRootEntries()); + int index = 0; + newTree->SetParent(thread, index, JSTaggedValue(-1)); + int child = 1; + while (!entries.empty()) { + int parent = entries.front(); + JSTaggedValue left = tree->GetLeftChild(parent); + if (!left.IsHole()) { + entries.push(left.GetInt()); + newTree->SetLeftChild(thread, index, JSTaggedValue(child)); + newTree->SetParent(thread, child++, JSTaggedValue(index)); + } + JSTaggedValue right = tree->GetRightChild(parent); + if (!right.IsHole()) { + entries.push(right.GetInt()); + newTree->SetRightChild(thread, index, JSTaggedValue(child)); + newTree->SetParent(thread, child++, JSTaggedValue(index)); + } + tree->CopyEntry(thread, parent, newTree, index); + entries.pop(); + index++; + } + return newTree; +} + +template +void TaggedTree::Transplant(const JSThread *thread, int dst, int src) +{ + int parent = GetParent(dst); + if (parent < 0) { + SetRootEntries(thread, src); + } else if (IsLeft(dst)) { + JSTaggedValue child = src < 0 ? JSTaggedValue::Hole() : JSTaggedValue(src); + SetLeftChild(thread, parent, child); + } else { + JSTaggedValue child = src < 0 ? JSTaggedValue::Hole() : JSTaggedValue(src); + SetRightChild(thread, parent, child); + } + SetParent(thread, src, JSTaggedValue(parent)); +} + +template +void TaggedTree::Remove(const JSThread *thread, const JSHandle &tree, int entry) +{ + int successor = entry; + if (!tree->GetLeftChild(entry).IsHole() && !tree->GetRightChild(entry).IsHole()) { + successor = tree->GetSuccessor(entry); + tree->CopyData(thread, entry, successor); + } + JSTaggedValue left = tree->GetLeftChild(successor); + JSTaggedValue right = tree->GetRightChild(successor); + int child = left.IsHole() ? (right.IsHole() ? -1 : right.GetInt()) : left.GetInt(); + if (child < 0) { + if (tree->GetColor(successor) == TreeColor::BLACK) { + tree->DeleteRebalance(thread, successor); + } + } + tree->Transplant(thread, successor, child); + + if (child >= 0) { + if (tree->GetColor(successor) == TreeColor::BLACK) { + tree->DeleteRebalance(thread, child); + } + } + tree->RemoveEntry(thread, successor); + tree->SetNumberOfElements(thread, tree->NumberOfElements() - 1); + tree->SetNumberOfDeletedElements(thread, tree->NumberOfDeletedElements() + 1); +} + +template +void TaggedTree::DeleteRebalance(const JSThread *thread, int index) +{ + while (index != GetRootEntries() && GetColor(index) == TreeColor::BLACK) { + if (IsLeft(index)) { + int bro = GetLeftBrother(index); + if (GetColor(bro)) { + SetColor(thread, bro, TreeColor::BLACK); + SetColor(thread, GetParent(index), TreeColor::RED); + LeftRotate(thread, GetParent(index)); + bro = GetLeftBrother(index); + } + if (GetColor(GetLeftChildIndex(bro)) == TreeColor::BLACK && + GetColor(GetRightChildIndex(bro)) == TreeColor::BLACK) { + SetColor(thread, bro, TreeColor::RED); + index = GetParent(index); + } else { + if (GetColor(GetRightChildIndex(bro)) == TreeColor::BLACK) { + SetColor(thread, GetLeftChildIndex(bro), TreeColor::BLACK); + SetColor(thread, bro, TreeColor::RED); + RightRotate(thread, bro); + bro = GetLeftBrother(index); + } + SetColor(thread, bro, GetColor(GetParent(index))); + SetColor(thread, GetParent(index), TreeColor::BLACK); + SetColor(thread, GetRightChildIndex(bro), TreeColor::BLACK); + LeftRotate(thread, GetParent(index)); + index = GetRootEntries(); + } + } else { + int bro = GetRightBrother(index); + if (GetColor(bro)) { + SetColor(thread, bro, TreeColor::BLACK); + SetColor(thread, GetParent(index), TreeColor::RED); + RightRotate(thread, GetParent(index)); + bro = GetRightBrother(index); + } + if (GetColor(GetRightChildIndex(bro)) == TreeColor::BLACK && + GetColor(GetLeftChildIndex(bro)) == TreeColor::BLACK) { + SetColor(thread, bro, TreeColor::RED); + index = GetParent(index); + } else { + if (GetColor(GetLeftChildIndex(bro)) == TreeColor::BLACK) { + SetColor(thread, GetRightChildIndex(bro), TreeColor::BLACK); + SetColor(thread, bro, TreeColor::RED); + LeftRotate(thread, bro); + bro = GetRightBrother(index); + } + SetColor(thread, bro, GetColor(GetParent(index))); + SetColor(thread, GetParent(index), TreeColor::BLACK); + SetColor(thread, GetLeftChildIndex(bro), TreeColor::BLACK); + RightRotate(thread, GetParent(index)); + index = GetRootEntries(); + } + } + } + SetColor(thread, index, TreeColor::BLACK); +} + +template +int TaggedTree::GetPreDecessor(int entry) const +{ + int child = GetLeftChildIndex(entry); + if (child >= 0) { + return GetMaximum(child); + } + int parent = GetParent(entry); + while (parent >= 0 && (GetLeftChildIndex(parent) == entry)) { + entry = parent; + parent = GetParent(entry); + } + return parent; +} + +template +int TaggedTree::GetSuccessor(int entry) const +{ + int child = GetRightChildIndex(entry); + if (child >= 0) { + return GetMinimum(child); + } + int parent = GetParent(entry); + while (parent >= 0 && (GetRightChildIndex(parent) == entry)) { + entry = parent; + parent = GetParent(entry); + } + return parent; +} + +template +int TaggedTree::FindEntry(JSThread *thread, const JSHandle &tree, const JSHandle &key) +{ + int parentIndex = tree->GetRootEntries(); + JSMutableHandle parentKey(thread, tree->GetKey(parentIndex)); + ComparisonResult res; + while (!parentKey->IsHole()) { + res = EntryCompare(thread, key, parentKey, tree); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + if (res == ComparisonResult::EQUAL) { + return parentIndex; + } else if (res == ComparisonResult::LESS) { + JSTaggedValue child = tree->GetLeftChild(parentIndex); + if (child.IsHole()) break; + parentIndex = child.GetInt(); + } else { + JSTaggedValue child = tree->GetRightChild(parentIndex); + if (child.IsHole()) break; + parentIndex = child.GetInt(); + } + parentKey.Update(tree->GetKey(parentIndex)); + } + return -1; +} + +template +JSHandle TaggedTree::GetSortArray(const JSThread *thread, const JSHandle &tree) +{ + JSHandle sortArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(tree->NumberOfElements()); + CStack entries; + int index = tree->GetRootEntries(); + int aid = 0; + while (index >= 0 || !entries.empty()) { + while (index >= 0) { + entries.emplace(index); + index = tree->GetLeftChildIndex(index); + } + if (!entries.empty()) { + sortArray->Set(thread, aid++, JSTaggedValue(entries.top())); + index = tree->GetRightChildIndex(entries.top()); + entries.pop(); + } + } + return sortArray; +} + +template +JSHandle TaggedTree::Insert(JSThread *thread, JSHandle &tree, + const JSHandle &key, const JSHandle &value) +{ + ASSERT(IsKey(key.GetTaggedValue())); + JSMutableHandle parentKey(thread, tree->GetRootKey()); + if (parentKey->IsHole()) { + tree->SetRoot(thread, 0, key.GetTaggedValue(), value.GetTaggedValue()); + tree->SetNumberOfElements(thread, tree->NumberOfElements() + 1); + return tree; + } + + JSHandle newTree = GrowCapacity(thread, tree); + int parentIndex = newTree->GetRootEntries(); + ComparisonResult res; + while (!parentKey->IsHole()) { + res = EntryCompare(thread, key, parentKey, tree); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + if (res == ComparisonResult::EQUAL) { + newTree->SetValue(thread, parentIndex, value.GetTaggedValue()); + return tree; + } else if (res == ComparisonResult::LESS) { + JSTaggedValue child = newTree->GetLeftChild(parentIndex); + if (child.IsHole()) break; + parentIndex = child.GetInt(); + } else { + JSTaggedValue child = newTree->GetRightChild(parentIndex); + if (child.IsHole()) break; + parentIndex = child.GetInt(); + } + parentKey.Update(newTree->GetKey(parentIndex)); + } + + int entry = newTree->NumberOfElements() + newTree->NumberOfDeletedElements(); + if (static_cast(res)) { + newTree->InsertRightEntry(thread, parentIndex, entry, key.GetTaggedValue(), value.GetTaggedValue()); + } else { + newTree->InsertLeftEntry(thread, parentIndex, entry, key.GetTaggedValue(), value.GetTaggedValue()); + } + newTree->SetNumberOfElements(thread, newTree->NumberOfElements() + 1); + newTree->InsertRebalance(thread, entry); + return newTree; +} + +template +JSHandle TaggedTree::GrowCapacity(const JSThread *thread, JSHandle &tree) +{ + int nof = tree->NumberOfElements() + tree->NumberOfDeletedElements(); + int oldCapacity = tree->Capacity(); + if (nof + 1 <= oldCapacity) { + return tree; + } + + int newCapacity = ComputeCapacity(oldCapacity); + int length = ELEMENTS_START_INDEX + newCapacity * (Derived::ENTRY_SIZE); + JSHandle newTree = AdjustTaggedTree(thread, tree, length); + newTree->SetCapacity(thread, newCapacity); + return newTree; +} + +template +JSTaggedValue TaggedTree::GetLowerKey(JSThread *thread, const JSHandle &tree, + const JSHandle &key) +{ + int parentIndex = tree->GetRootEntries(); + JSMutableHandle parentKey(thread, tree->GetKey(parentIndex)); + int resultIndex = -1; + ComparisonResult res; + while (parentIndex >= 0) { + res = EntryCompare(thread, key, parentKey, tree); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (res == ComparisonResult::GREAT) { + resultIndex = parentIndex; + parentIndex = tree->GetRightChildIndex(parentIndex); + } else { + parentIndex = tree->GetLeftChildIndex(parentIndex); + } + parentKey.Update(tree->GetKey(parentIndex)); + } + JSTaggedValue lowerKey = tree->GetKey(resultIndex); + return tree->Transform(lowerKey); +} + +template +JSTaggedValue TaggedTree::GetHigherKey(JSThread *thread, const JSHandle &tree, + const JSHandle &key) +{ + int parentIndex = tree->GetRootEntries(); + JSMutableHandle parentKey(thread, tree->GetKey(parentIndex)); + int resultIndex = -1; + ComparisonResult res; + while (parentIndex >= 0) { + res = EntryCompare(thread, key, parentKey, tree); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (res == ComparisonResult::LESS) { + resultIndex = parentIndex; + parentIndex = tree->GetLeftChildIndex(parentIndex); + } else { + parentIndex = tree->GetRightChildIndex(parentIndex); + } + parentKey.Update(tree->GetKey(parentIndex)); + } + JSTaggedValue lowerKey = tree->GetKey(resultIndex); + return tree->Transform(lowerKey); +} + +template +JSHandle TaggedTree::Shrink(const JSThread *thread, const JSHandle &tree) +{ + int oldCapacity = tree->Capacity(); + if (tree->NumberOfElements() >= (oldCapacity + 1) / 4) { // 4: quarter + return tree; + } + int newCapacity = (oldCapacity - 1) >> 1; + if (newCapacity < Derived::MIN_SHRINK_CAPACITY) { + return tree; + } + + int length = ELEMENTS_START_INDEX + newCapacity * (Derived::ENTRY_SIZE); + JSHandle newTree = AdjustTaggedTree(thread, tree, length); + newTree->SetCapacity(thread, newCapacity); + return newTree; +} + +// TaggedTreeMap +JSTaggedValue TaggedTreeMap::Create(const JSThread *thread, int numberOfElements) +{ + return RBTree::Create(thread, numberOfElements).GetTaggedValue(); +} + +JSHandle TaggedTreeMap::GetArrayFromMap(const JSThread *thread, const JSHandle &map) +{ + return RBTree::GetSortArray(thread, map); +} + +JSTaggedValue TaggedTreeMap::Set(JSThread *thread, JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + return RBTree::Insert(thread, obj, key, value).GetTaggedValue(); +} + +JSTaggedValue TaggedTreeMap::Delete(JSThread *thread, const JSHandle &map, int entry) +{ + RBTree::Remove(thread, map, entry); + return RBTree::Shrink(thread, map).GetTaggedValue(); +} + +bool TaggedTreeMap::HasValue(const JSThread *thread, JSTaggedValue value) const +{ + int root = GetRootEntries(); + if (root < 0) { + return false; + } + + CQueue entries; + entries.push(root); + while (!entries.empty()) { + int parent = entries.front(); + if (JSTaggedValue::SameValue(GetValue(parent), value)) { + return true; + } + int left = GetLeftChildIndex(parent); + if (left >= 0) { + entries.push(left); + } + int right = GetRightChildIndex(parent); + if (right >= 0) { + entries.push(right); + } + entries.pop(); + } + return false; +} + +JSTaggedValue TaggedTreeMap::SetAll(JSThread *thread, JSHandle &dst, const JSHandle &src) +{ + CQueue entries; + entries.push(src->GetRootEntries()); + JSMutableHandle map(dst); + while (!entries.empty()) { + int parent = entries.front(); + auto tmap = Insert(thread, map, JSHandle(thread, src->GetKey(parent)), + JSHandle(thread, src->GetValue(parent))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + map.Update(tmap.GetTaggedValue()); + int left = src->GetLeftChildIndex(parent); + if (left >= 0) { + entries.push(left); + } + int right = src->GetRightChildIndex(parent); + if (right >= 0) { + entries.push(right); + } + entries.pop(); + } + return map.GetTaggedValue(); +} + +JSTaggedValue TaggedTreeMap::GetLowerKey(JSThread *thread, const JSHandle &map, + const JSHandle &key) +{ + return RBTree::GetLowerKey(thread, map, key); +} + +JSTaggedValue TaggedTreeMap::GetHigherKey(JSThread *thread, const JSHandle &map, + const JSHandle &key) +{ + return RBTree::GetHigherKey(thread, map, key); +} + +int TaggedTreeMap::FindEntry(JSThread *thread, const JSHandle &map, const JSHandle &key) +{ + return RBTree::FindEntry(thread, map, key); +} + +// TaggedTreeSet +JSTaggedValue TaggedTreeSet::Create(const JSThread *thread, int numberOfElements) +{ + return RBTree::Create(thread, numberOfElements).GetTaggedValue(); +} + +JSHandle TaggedTreeSet::GetArrayFromSet(const JSThread *thread, const JSHandle &set) +{ + return RBTree::GetSortArray(thread, set); +} + +JSTaggedValue TaggedTreeSet::Add(JSThread *thread, JSHandle &obj, const JSHandle &value) +{ + return RBTree::Insert(thread, obj, value, value).GetTaggedValue(); +} + +JSTaggedValue TaggedTreeSet::Delete(JSThread *thread, const JSHandle &set, int entry) +{ + RBTree::Remove(thread, set, entry); + return RBTree::Shrink(thread, set).GetTaggedValue(); +} + +JSTaggedValue TaggedTreeSet::GetLowerKey(JSThread *thread, const JSHandle &set, + const JSHandle &key) +{ + return RBTree::GetLowerKey(thread, set, key); +} + +JSTaggedValue TaggedTreeSet::GetHigherKey(JSThread *thread, const JSHandle &set, + const JSHandle &key) +{ + return RBTree::GetHigherKey(thread, set, key); +} + +int TaggedTreeSet::FindEntry(JSThread *thread, const JSHandle &set, const JSHandle &key) +{ + return RBTree::FindEntry(thread, set, key); +} +} // namespace panda::ecmascript diff --git a/ecmascript/tagged_tree.h b/ecmascript/tagged_tree.h new file mode 100644 index 0000000000..953d377248 --- /dev/null +++ b/ecmascript/tagged_tree.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_TAGGED_TREE_H +#define ECMASCRIPT_TAGGED_TREE_H + +#include "ecmascript/js_tagged_value.h" +#include "js_handle.h" +#include "tagged_array.h" + +namespace panda::ecmascript { +enum TreeColor : uint8_t { BLACK = 0, RED }; +/** + * The tree layout is as follows: + * 1.array[0-4] is used to store common information, sush as: + * +------------------------+-----------------------------+------------------------+------------+------------------+ + * | the number of elements | the number of hole elements | the number of capacity | root index | compare function | + * +------------------------+-----------------------------+------------------------+------------+------------------+ + * 2.array[5,5+capacity] is used to store node of tree, map and set store nodes in different formats respectively. + * */ +template +class TaggedTree : public TaggedArray { +public: + static constexpr int MIN_CAPACITY = 7; + static constexpr int NUMBER_OF_ELEMENTS_INDEX = 0; + static constexpr int NUMBER_OF_HOLE_ENTRIES_INDEX = 1; + static constexpr int CAPACITY_INDEX = 2; + static constexpr int ROOT_INDEX = 3; + static constexpr int COMPARE_FUNCTION_INDEX = 4; + static constexpr int ELEMENTS_START_INDEX = 5; + static constexpr int MIN_SHRINK_CAPACITY = 15; + + static JSHandle Create(const JSThread *thread, int numberOfElements); + + static JSHandle Insert(JSThread *thread, JSHandle &tree, const JSHandle &key, + const JSHandle &value); + + static JSHandle GrowCapacity(const JSThread *thread, JSHandle &tree); + + inline static int ComputeCapacity(int oldCapacity); + + static void Remove(const JSThread *thread, const JSHandle &tree, int entry); + + inline int NumberOfElements() const; + inline int NumberOfDeletedElements() const; + + inline int Capacity() const; + + inline JSTaggedValue GetKey(int entry) const; + inline JSTaggedValue GetValue(int entry) const; + + inline TreeColor GetColor(int entry) const; + + inline void SetCapacity(const JSThread *thread, int capacity); + + inline static bool IsKey(JSTaggedValue key) + { + return !key.IsHole(); + } + + inline void SetNumberOfElements(const JSThread *thread, int num); + inline void SetNumberOfDeletedElements(const JSThread *thread, int num); + + inline void SetRootEntries(const JSThread *thread, int num); + inline int GetRootEntries() const; + + static int FindEntry(JSThread *thread, const JSHandle &tree, const JSHandle &key); + + inline static ComparisonResult EntryCompare(JSThread *thread, const JSHandle valueX, + const JSHandle valueY, JSHandle tree); + + inline void SetKey(const JSThread *thread, uint32_t entry, JSTaggedValue key); + inline void SetValue(const JSThread *thread, uint32_t entry, JSTaggedValue value); + inline void SetCompare(const JSThread *thread, JSTaggedValue fn); + inline JSTaggedValue GetCompare() const; + + inline int GetMinimum(int entry) const; + inline int GetMaximum(int entry) const; + + inline int GetParent(int entry) const; + inline JSTaggedValue GetLeftChild(int parent) const; + inline JSTaggedValue GetRightChild(int parent) const; + inline int GetLeftChildIndex(int parent) const; + inline int GetRightChildIndex(int parent) const; + +protected: + inline JSTaggedValue GetElement(int index) const; + + // get root + inline JSTaggedValue GetRootKey() const; + + inline void SetRoot(JSThread *thread, int index, JSTaggedValue key, JSTaggedValue value); + + inline void SetElement(const JSThread *thread, uint32_t index, JSTaggedValue element); + inline void SetColor(const JSThread *thread, int entry, TreeColor color); + inline void SetParent(const JSThread *thread, int entry, JSTaggedValue value); + + inline uint32_t EntryToIndex(uint32_t entry) const; + inline void InsertLeftEntry(const JSThread *thread, uint32_t parentIndex, uint32_t entry, JSTaggedValue key, + JSTaggedValue value); + inline void InsertRightEntry(const JSThread *thread, uint32_t parentIndex, uint32_t entry, JSTaggedValue key, + JSTaggedValue value); + + void InsertRebalance(const JSThread *thread, int index); + void DeleteRebalance(const JSThread *thread, int index); + inline bool IsVaildIndex(int entry) const; + + inline int GetLeftBrother(int entry) const; + inline int GetRightBrother(int entry) const; + + inline bool IsLeft(int entry) const; + inline bool IsRight(int entry) const; + + int GetPreDecessor(int entry) const; + int GetSuccessor(int entry) const; + + inline void SetLeftChild(const JSThread *thread, uint32_t entry, JSTaggedValue value); + inline void SetRightChild(const JSThread *thread, uint32_t entry, JSTaggedValue value); + + void LeftRotate(const JSThread *thread, int index); + void RightRotate(const JSThread *thread, int index); + + static JSHandle AdjustTaggedTree(const JSThread *thread, const JSHandle &tree, int len); + inline static ComparisonResult OrdinayEntryCompare(JSThread *thread, const JSHandle valueX, + const JSHandle valueY); + + inline void CopyEntry(const JSThread *thread, int parent, const JSHandle &newTree, int index); + inline void CopyData(const JSThread *thread, int dst, int src); + inline void CopyAllData(const JSThread *thread, int parent, const JSHandle &newTree, int index); + + inline void RemoveEntry(const JSThread *thread, int index); + + inline JSTaggedValue Transform(JSTaggedValue v) const; + void Transplant(const JSThread *thread, int dst, int src); + + static JSTaggedValue GetLowerKey(JSThread *thread, const JSHandle &tree, + const JSHandle &key); + static JSTaggedValue GetHigherKey(JSThread *thread, const JSHandle &tree, + const JSHandle &key); + static JSHandle GetSortArray(const JSThread *thread, const JSHandle &tree); + static JSHandle Shrink(const JSThread *thread, const JSHandle &tree); +}; + + +class TaggedTreeMap : public TaggedTree { +public: + using RBTree = TaggedTree; + static TaggedTreeMap *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + + static JSTaggedValue Create(const JSThread *thread, int numberOfElements = MIN_CAPACITY); + static JSTaggedValue Set(JSThread *thread, JSHandle &obj, + const JSHandle &key, const JSHandle &value); + inline static JSTaggedValue Get(JSThread *thread, const JSHandle &map, + const JSHandle &key); + static JSTaggedValue Delete(JSThread *thread, const JSHandle &map, int entry); + bool HasValue(const JSThread *thread, JSTaggedValue value) const; + + static JSTaggedValue GetLowerKey(JSThread *thread, const JSHandle &map, + const JSHandle &key); + static JSTaggedValue GetHigherKey(JSThread *thread, const JSHandle &map, + const JSHandle &key); + + inline JSTaggedValue GetFirstKey() const; + inline JSTaggedValue GetLastKey() const; + + static JSTaggedValue SetAll(JSThread *thread, JSHandle &dst, const JSHandle &src); + static JSHandle GetArrayFromMap(const JSThread *thread, const JSHandle &map); + static int FindEntry(JSThread *thread, const JSHandle &map, const JSHandle &key); + + static const int ENTRY_SIZE = 6; + static const int ENTRY_VALUE_INDEX = 1; + static const int ENTRY_COLOR_INDEX = 2; + static const int ENTRY_PARENT_INDEX = 3; + static const int ENTRY_LEFT_CHILD_INDEX = 4; + static const int ENTRY_RIGHT_CHILD_INDEX = 5; + DECL_DUMP() + + inline void CopyEntry(const JSThread *thread, int parent, const JSHandle &newTree, int index); + inline void CopyData(const JSThread *thread, int dst, int src); + inline void RemoveEntry(const JSThread *thread, int index); +}; + +class TaggedTreeSet : public TaggedTree { +public: + using RBTree = TaggedTree; + static TaggedTreeSet *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + + static JSTaggedValue Create(const JSThread *thread, int numberOfElements = MIN_CAPACITY); + static JSTaggedValue Add(JSThread *thread, JSHandle &obj, const JSHandle &value); + static JSTaggedValue Delete(JSThread *thread, const JSHandle &set, int entry); + + static JSTaggedValue GetLowerKey(JSThread *thread, const JSHandle &set, + const JSHandle &key); + static JSTaggedValue GetHigherKey(JSThread *thread, const JSHandle &set, + const JSHandle &key); + + inline JSTaggedValue GetFirstKey() const; + inline JSTaggedValue GetLastKey() const; + static JSHandle GetArrayFromSet(const JSThread *thread, const JSHandle &set); + static int FindEntry(JSThread *thread, const JSHandle &set, const JSHandle &key); + + static const int ENTRY_SIZE = 5; + static const int ENTRY_VALUE_INDEX = 0; + static const int ENTRY_COLOR_INDEX = 1; + static const int ENTRY_PARENT_INDEX = 2; + static const int ENTRY_LEFT_CHILD_INDEX = 3; + static const int ENTRY_RIGHT_CHILD_INDEX = 4; + + DECL_DUMP() + + inline void CopyEntry(const JSThread *thread, int parent, const JSHandle &newTree, int index); + inline void CopyData(const JSThread *thread, int dst, int src); + inline void RemoveEntry(const JSThread *thread, int index); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_TREE_H diff --git a/ecmascript/tests/BUILD.gn b/ecmascript/tests/BUILD.gn index 12e765b71c..22d381a7da 100644 --- a/ecmascript/tests/BUILD.gn +++ b/ecmascript/tests/BUILD.gn @@ -664,6 +664,33 @@ host_unittest_action("JsVerificationTest") { } } +host_unittest_action("TaggedTreeTest") { + module_out_path = module_output_path + + sources = [ + # test file + "tagged_tree_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_common_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime:libark_jsruntime_test", + sdk_libc_secshared_dep, + ] + + if (!is_standard_system) { + deps += [ "$ark_root/runtime:libarkruntime" ] + } +} + host_unittest_action("HugeObjectTest") { module_out_path = module_output_path @@ -1082,6 +1109,7 @@ group("unittest") { ":NativePointerTest", ":ObjectFactoryTest", ":SymbolTableTest", + ":TaggedTreeTest", ":TaggedValueTest", ":WeakRefGenGcTest", ":WeakRefStwGcTest", @@ -1127,6 +1155,7 @@ group("host_unittest") { ":NativePointerTestAction", ":ObjectFactoryTestAction", ":SymbolTableTestAction", + ":TaggedTreeTestAction", ":TaggedValueTestAction", ":WeakRefGenGcTestAction", ":WeakRefStwGcTestAction", diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index e890642570..e521bf1c98 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -30,6 +30,10 @@ #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_tree_map.h" +#include "ecmascript/js_api_tree_map_iterator.h" +#include "ecmascript/js_api_tree_set.h" +#include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_arguments.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraylist.h" @@ -74,6 +78,7 @@ #include "ecmascript/object_factory.h" #include "ecmascript/tagged_array.h" #include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_tree-inl.h" #include "ecmascript/template_map.h" #include "ecmascript/tests/test_helper.h" #include "ecmascript/transitions_dictionary.h" @@ -145,6 +150,28 @@ static JSHandle NewJSSet(JSThread *thread, ObjectFactory *factory, JSHand return jsSet; } +static JSHandle NewJSAPITreeMap(JSThread *thread, ObjectFactory *factory) +{ + auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proto = globalEnv->GetObjectFunctionPrototype(); + JSHandle mapClass = factory->NewEcmaDynClass(JSAPITreeMap::SIZE, JSType::JS_API_TREE_MAP, proto); + JSHandle jsTreeMap = JSHandle::Cast(factory->NewJSObject(mapClass)); + JSHandle treeMap(thread, TaggedTreeMap::Create(thread)); + jsTreeMap->SetTreeMap(thread, treeMap); + return jsTreeMap; +} + +static JSHandle NewJSAPITreeSet(JSThread *thread, ObjectFactory *factory) +{ + auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proto = globalEnv->GetObjectFunctionPrototype(); + JSHandle setClass = factory->NewEcmaDynClass(JSAPITreeSet::SIZE, JSType::JS_API_TREE_SET, proto); + JSHandle jsTreeSet = JSHandle::Cast(factory->NewJSObject(setClass)); + JSHandle treeSet(thread, TaggedTreeSet::Create(thread)); + jsTreeSet->SetTreeSet(thread, treeSet); + return jsTreeSet; +} + static JSHandle NewJSObject(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv) { JSFunction *jsFunc = globalEnv->GetObjectFunction().GetObject(); @@ -623,6 +650,32 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) // unused break; } + case JSType::JS_API_TREE_MAP: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSAPITreeMap::SIZE, 1) + JSHandle jsTreeMap = NewJSAPITreeMap(thread, factory); + DUMP_FOR_HANDLE(jsTreeMap) + break; + } + case JSType::JS_API_TREE_SET: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSAPITreeSet::SIZE, 1) + JSHandle jsTreeSet = NewJSAPITreeSet(thread, factory); + DUMP_FOR_HANDLE(jsTreeSet) + break; + } + case JSType::JS_API_TREEMAP_ITERATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSAPITreeMapIterator::SIZE, 4) + JSHandle jsTreeMapIter = + factory->NewJSAPITreeMapIterator(NewJSAPITreeMap(thread, factory), IterationKind::KEY); + DUMP_FOR_HANDLE(jsTreeMapIter) + break; + } + case JSType::JS_API_TREESET_ITERATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSAPITreeSetIterator::SIZE, 4) + JSHandle jsTreeSetIter = + factory->NewJSAPITreeSetIterator(NewJSAPITreeSet(thread, factory), IterationKind::KEY); + DUMP_FOR_HANDLE(jsTreeSetIter) + break; + } default: LOG_ECMA_MEM(ERROR) << "JSType " << static_cast(type) << " cannot be dumped."; UNREACHABLE(); diff --git a/ecmascript/tests/tagged_tree_test.cpp b/ecmascript/tests/tagged_tree_test.cpp new file mode 100644 index 0000000000..02aa1138b1 --- /dev/null +++ b/ecmascript/tests/tagged_tree_test.cpp @@ -0,0 +1,1108 @@ +/* + * Copyright (c) 2021 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 +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_tree-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class TaggedTreeTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + JSHandle GetGlobalEnv() + { + EcmaVM *ecma = thread->GetEcmaVM(); + return ecma->GetGlobalEnv(); + } + class TestClass : public base::BuiltinsBase { + public: + 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 xString = static_cast(valueX->GetTaggedObject()); + auto yString = static_cast(valueY->GetTaggedObject()); + int result = xString->Compare(yString); + 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)); + } + }; +}; + +template +bool CheckRBTreeOfAllPaths(JSHandle &tree, int numsOfBlack, int index, int count) +{ + if (index < 0) { + return count == numsOfBlack; + } + if (tree->GetColor(index) == TreeColor::BLACK) { + count++; + } + if (CheckRBTreeOfAllPaths(tree, numsOfBlack, tree->GetLeftChildIndex(index), count) && + CheckRBTreeOfAllPaths(tree, numsOfBlack, tree->GetRightChildIndex(index), count)) { + return true; + } + return false; +} + +template +bool CheckBlackNodeNumbers(JSHandle &tree, int index) +{ + int numsOfBlack = 0; + int child = index; + while (child >= 0) { + if (tree->GetColor(child) == TreeColor::BLACK) { + numsOfBlack++; + } + child = tree->GetLeftChildIndex(child); + } + return CheckRBTreeOfAllPaths(tree, numsOfBlack, index, 0); +} + +template +bool IsVaildRBTree(JSThread *thread, JSHandle &tree, int nodeIndex) +{ + CQueue nodes; + nodes.emplace(nodeIndex); + while (!nodes.empty()) { + int index = nodes.front(); + int parent = tree->GetParent(index); + int pleft = tree->GetLeftChildIndex(parent); + int pright = tree->GetRightChildIndex(parent); + if (parent >= 0 && index != pleft && index != pright) { + return false; + } + + int ileft = tree->GetLeftChildIndex(index); + JSHandle indexKey(thread, tree->GetKey(index)); + if (ileft >= 0) { + JSHandle leftKey(thread, tree->GetKey(ileft)); + ComparisonResult result = TaggedTree::EntryCompare(thread, leftKey, indexKey, tree); + if (tree->GetParent(ileft) != index || result != ComparisonResult::LESS) { + return false; + } + nodes.emplace(ileft); + } + int iright = tree->GetRightChildIndex(index); + if (iright >= 0) { + JSHandle rightKey(thread, tree->GetKey(iright)); + ComparisonResult result = TaggedTree::EntryCompare(thread, rightKey, indexKey, tree); + if (tree->GetParent(iright) != index || result != ComparisonResult::GREAT) { + return false; + } + nodes.emplace(iright); + } + + // check red node + TreeColor indexColor = tree->GetColor(index); + if (indexColor == TreeColor::RED) { + if (ileft >= 0 && tree->GetColor(ileft) == TreeColor::RED) { + return false; + } + if (iright >= 0 && tree->GetColor(iright) == TreeColor::RED) { + return false; + } + } + // check black node + if (!CheckBlackNodeNumbers(tree, index)) { + return false; + } + nodes.pop(); + } + return true; +} + +HWTEST_F_L0(TaggedTreeTest, TreeMapCreate) +{ + int numOfElement = 64; + JSHandle tmap(thread, TaggedTreeMap::Create(thread, numOfElement)); + EXPECT_TRUE(tmap->Capacity() == numOfElement); + EXPECT_TRUE(tmap->GetRootEntries() == -1); + EXPECT_TRUE(tmap->NumberOfElements() == 0); + EXPECT_TRUE(tmap->NumberOfDeletedElements() == 0); +} + +HWTEST_F_L0(TaggedTreeTest, TreeSetCreate) +{ + int numOfElement = 64; + JSHandle tset(thread, TaggedTreeSet::Create(thread, numOfElement)); + EXPECT_TRUE(tset->Capacity() == numOfElement); + EXPECT_TRUE(tset->GetRootEntries() == -1); + EXPECT_TRUE(tset->NumberOfElements() == 0); + EXPECT_TRUE(tset->NumberOfDeletedElements() == 0); +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapAddKeyAndValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 64; + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread, numOfElement)); + JSHandle objFun = GetGlobalEnv()->GetObjectFunction(); + + char keyArray[] = "mykey1"; + JSHandle stringKey1 = factory->NewFromCanBeCompressString(keyArray); + JSHandle key1(stringKey1); + JSHandle value1(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + char key2Array[] = "mykey2"; + JSHandle stringKey2 = factory->NewFromCanBeCompressString(key2Array); + JSHandle key2(stringKey2); + JSHandle value2(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + // test set() + tmap.Update(TaggedTreeMap::Set(thread, tmap, key1, value1)); + EXPECT_EQ(tmap->NumberOfElements(), 1); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key2, value2)); + EXPECT_EQ(tmap->NumberOfElements(), 2); + + // test find() + JSTaggedValue res = TaggedTreeMap::Get(thread, tmap, key1); + EXPECT_EQ(value1.GetTaggedValue(), res); + + // test remove() + int entry = TaggedTreeMap::FindEntry(thread, tmap, key1); + res = TaggedTreeMap::Delete(thread, tmap, entry); + EXPECT_EQ(TaggedTreeMap::Cast(res.GetTaggedObject())->NumberOfElements(), 1); + tmap.Update(res); + EXPECT_EQ(tmap->NumberOfElements(), 1); + res = TaggedTreeMap::Get(thread, tmap, key1); + EXPECT_EQ(res, JSTaggedValue::Undefined()); +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeSetAddValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 64; + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread, numOfElement)); + + char keyArray[] = "mykey1"; + JSHandle stringKey1 = factory->NewFromCanBeCompressString(keyArray); + JSHandle key1(stringKey1); + char key2Array[] = "mykey2"; + JSHandle stringKey2 = factory->NewFromCanBeCompressString(key2Array); + JSHandle key2(stringKey2); + + // test set() + tset.Update(TaggedTreeSet::Add(thread, tset, key1)); + EXPECT_EQ(tset->NumberOfElements(), 1); + tset.Update(TaggedTreeSet::Add(thread, tset, key2)); + EXPECT_EQ(tset->NumberOfElements(), 2); + + // test find() + int entry = TaggedTreeSet::FindEntry(thread, tset, key1); + EXPECT_TRUE(entry >= 0); + + // test remove() + JSTaggedValue res = TaggedTreeSet::Delete(thread, tset, entry); + EXPECT_EQ(TaggedTreeSet::Cast(res.GetTaggedObject())->NumberOfElements(), 1); + EXPECT_EQ(-1, TaggedTreeSet::FindEntry(thread, tset, key1)); + EXPECT_EQ(tset->NumberOfElements(), 1); +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapGrowCapacity) +{ + constexpr uint32_t NODE_NUMBERS = 32; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + char keyArray[7] = "mykey"; // 7 means array length + for (int i = 0; i < NODE_NUMBERS; i++) { + keyArray[5] = '1' + i; // 5 means index of keyArray + keyArray[6] = 0; // 6 means index of keyArray + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + JSHandle value(thread, JSTaggedValue(i)); + // test set() + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + EXPECT_TRUE(TaggedTreeMap::FindEntry(thread, tmap, key) >= 0); + } + + for (int i = 0; i < NODE_NUMBERS; i++) { + keyArray[5] = '1' + i; // 5 means index of keyArray + keyArray[6] = 0; // 6 means index of keyArray + JSHandle stringKey(factory->NewFromCanBeCompressString(keyArray)); + // test get() + JSTaggedValue res = TaggedTreeMap::Get(thread, tmap, stringKey); + EXPECT_EQ(JSTaggedValue(i), res); + } + EXPECT_EQ(tmap->NumberOfElements(), NODE_NUMBERS); + EXPECT_EQ(tmap->Capacity(), 63); // 63 means capacity after Grow +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeSetGrowCapacity) +{ + constexpr uint32_t NODE_NUMBERS = 32; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + // create key + char keyArray[7] = "mykey"; // 7 means array length + for (int i = 0; i < NODE_NUMBERS; i++) { + keyArray[5] = '1' + i; // 5 means index of keyArray + keyArray[6] = 0; // 6 means index of keyArray + JSHandle stringKey = factory->NewFromCanBeCompressString(keyArray); + JSHandle key(stringKey); + + // test set() + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + EXPECT_TRUE(TaggedTreeSet::FindEntry(thread, tset, key) >= 0); + } + + for (int i = 0; i < NODE_NUMBERS; i++) { + keyArray[5] = '1' + i; // 5 means index of keyArray + keyArray[6] = 0; // 6 means index of keyArray + JSHandle stringKey(factory->NewFromCanBeCompressString(keyArray)); + // test get() + EXPECT_TRUE(TaggedTreeSet::FindEntry(thread, tset, stringKey) >= 0); + } + EXPECT_EQ(tset->NumberOfElements(), NODE_NUMBERS); + EXPECT_EQ(tset->Capacity(), 63); // 63 means capacity after Grow +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapHasValue) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + std::string myValue("myvalue"); + + // test TaggedTreeMap HasValue + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + EXPECT_EQ(tmap->NumberOfElements(), NODE_NUMBERS); + EXPECT_EQ(tmap->Capacity(), 15); // 15 means capacity after Grow + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + bool success = tmap->HasValue(thread, value.GetTaggedValue()); + EXPECT_TRUE(success); + } +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapGetLowerKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + std::string myValue("myvalue"); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + + std::string minKey = myKey + std::to_string(0); + key.Update(factory->NewFromStdString(minKey).GetTaggedValue()); + JSTaggedValue lowerKey = TaggedTreeMap::GetLowerKey(thread, tmap, key); + EXPECT_TRUE(lowerKey.IsUndefined()); + + // add [1, 1] + key.Update(JSTaggedValue(1)); + value.Update(JSTaggedValue(1)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + + key.Update(factory->NewFromStdString(minKey).GetTaggedValue()); + lowerKey = TaggedTreeMap::GetLowerKey(thread, tmap, key); + EXPECT_EQ(lowerKey, JSTaggedValue(1)); + + // check mykey1 ...mykeyn + JSMutableHandle keyToCompare(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 1; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + std::string tmp = myKey + std::to_string(i - 1); + keyToCompare.Update(factory->NewFromStdString(tmp).GetTaggedValue()); + lowerKey = TaggedTreeMap::GetLowerKey(thread, tmap, key); + EXPECT_EQ(lowerKey, keyToCompare.GetTaggedValue()); + } +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapGetHigherKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + + JSMutableHandle maxKey(thread, JSTaggedValue(NODE_NUMBERS - 1)); + JSTaggedValue higherKey = TaggedTreeMap::GetHigherKey(thread, tmap, maxKey); + EXPECT_TRUE(higherKey.IsUndefined()); + + // add [mykey, mykey] + std::string myKey("mykey"); + key.Update(factory->NewFromStdString(myKey).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + + higherKey = TaggedTreeMap::GetHigherKey(thread, tmap, maxKey); + EXPECT_EQ(higherKey, key.GetTaggedValue()); + + // check 1 ...n + JSMutableHandle keyToCompare(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS - 1; i++) { + key.Update(JSTaggedValue(i)); + keyToCompare.Update(JSTaggedValue(i + 1)); + higherKey = TaggedTreeMap::GetHigherKey(thread, tmap, key); + EXPECT_EQ(higherKey, keyToCompare.GetTaggedValue()); + } +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapGetFirsKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + std::string myValue("myvalue"); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + std::string ckey = myKey + std::to_string(0); + key.Update(factory->NewFromStdString(ckey).GetTaggedValue()); + JSTaggedValue firstKey = tmap->GetFirstKey(); + EXPECT_EQ(firstKey, key.GetTaggedValue()); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + firstKey = tmap->GetFirstKey(); + EXPECT_EQ(firstKey, JSTaggedValue(0)); +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapGetLastKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + JSTaggedValue lastKey = tmap->GetLastKey(); + EXPECT_EQ(lastKey, JSTaggedValue(NODE_NUMBERS - 1)); + + std::string myKey("mykey"); + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + std::string ckey = myKey + std::to_string(NODE_NUMBERS - 1); + key.Update(factory->NewFromStdString(ckey).GetTaggedValue()); + lastKey = tmap->GetLastKey(); + EXPECT_EQ(lastKey, key.GetTaggedValue()); +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapSetAll) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeMap + JSMutableHandle smap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + smap.Update(TaggedTreeMap::Set(thread, smap, key, value)); + } + + JSMutableHandle dmap(thread, TaggedTreeMap::Create(thread)); + std::string myKey("mykey"); + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + dmap.Update(TaggedTreeMap::Set(thread, dmap, key, value)); + } + dmap.Update(TaggedTreeMap::SetAll(thread, dmap, smap)); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + JSTaggedValue res = TaggedTreeMap::Get(thread, dmap, key); + EXPECT_EQ(res, value.GetTaggedValue()); + + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + res = TaggedTreeMap::Get(thread, dmap, key); + EXPECT_EQ(res, value.GetTaggedValue()); + } +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeMapGetArrayFromMap) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + + std::string myKey("mykey"); + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + JSHandle arr = TaggedTreeMap::GetArrayFromMap(thread, tmap); + EXPECT_EQ(arr->GetLength(), NODE_NUMBERS * 2); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + EXPECT_EQ(tmap->GetKey(arr->Get(i).GetInt()), JSTaggedValue(i)); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + EXPECT_EQ(tmap->GetKey(arr->Get(NODE_NUMBERS + i).GetInt()), key.GetTaggedValue()); + } +} + +// TaggedTreeSet +HWTEST_F_L0(TaggedTreeTest, TestTreeSetGetLowerKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + + std::string minKey = myKey + std::to_string(0); + key.Update(factory->NewFromStdString(minKey).GetTaggedValue()); + JSTaggedValue lowerKey = TaggedTreeSet::GetLowerKey(thread, tset, key); + EXPECT_TRUE(lowerKey.IsUndefined()); + + // add [1, 1] + key.Update(JSTaggedValue(1)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + + key.Update(factory->NewFromStdString(minKey).GetTaggedValue()); + lowerKey = TaggedTreeSet::GetLowerKey(thread, tset, key); + EXPECT_EQ(lowerKey, JSTaggedValue(1)); + + // check mykey1 ...mykeyn + JSMutableHandle keyToCompare(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 1; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + std::string tmp = myKey + std::to_string(i - 1); + keyToCompare.Update(factory->NewFromStdString(tmp).GetTaggedValue()); + lowerKey = TaggedTreeSet::GetLowerKey(thread, tset, key); + EXPECT_EQ(lowerKey, keyToCompare.GetTaggedValue()); + } +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeSetGetHigherKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + + JSMutableHandle maxKey(thread, JSTaggedValue(NODE_NUMBERS - 1)); + JSTaggedValue higherKey = TaggedTreeSet::GetHigherKey(thread, tset, maxKey); + EXPECT_TRUE(higherKey.IsUndefined()); + + // add [mykey, mykey] + std::string myKey("mykey"); + key.Update(factory->NewFromStdString(myKey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + + higherKey = TaggedTreeSet::GetHigherKey(thread, tset, maxKey); + EXPECT_EQ(higherKey, key.GetTaggedValue()); + + // check 1 ...n + JSMutableHandle keyToCompare(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS - 1; i++) { + key.Update(JSTaggedValue(i)); + keyToCompare.Update(JSTaggedValue(i + 1)); + higherKey = TaggedTreeSet::GetHigherKey(thread, tset, key); + EXPECT_EQ(higherKey, keyToCompare.GetTaggedValue()); + } +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeSetGetFirsKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + std::string ckey = myKey + std::to_string(0); + key.Update(factory->NewFromStdString(ckey).GetTaggedValue()); + JSTaggedValue firstKey = tset->GetFirstKey(); + EXPECT_EQ(firstKey, key.GetTaggedValue()); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + firstKey = tset->GetFirstKey(); + EXPECT_EQ(firstKey, JSTaggedValue(0)); +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeSetGetLastKey) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + JSTaggedValue lastKey = tset->GetLastKey(); + EXPECT_EQ(lastKey, JSTaggedValue(NODE_NUMBERS - 1)); + + std::string myKey("mykey"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + std::string ckey = myKey + std::to_string(NODE_NUMBERS - 1); + key.Update(factory->NewFromStdString(ckey).GetTaggedValue()); + lastKey = tset->GetLastKey(); + EXPECT_EQ(lastKey, key.GetTaggedValue()); +} + +HWTEST_F_L0(TaggedTreeTest, TestTreeSetGetArrayFromSet) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + + std::string myKey("mykey"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + JSHandle arr = TaggedTreeSet::GetArrayFromSet(thread, tset); + EXPECT_EQ(arr->GetLength(), NODE_NUMBERS * 2); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + EXPECT_EQ(tset->GetKey(arr->Get(i).GetInt()), JSTaggedValue(i)); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + EXPECT_EQ(tset->GetKey(arr->Get(NODE_NUMBERS + i).GetInt()), key.GetTaggedValue()); + } +} + +HWTEST_F_L0(TaggedTreeTest, TestSetAfterDelete) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + EXPECT_EQ(tmap->NumberOfElements(), NODE_NUMBERS); + EXPECT_EQ(tmap->NumberOfDeletedElements(), 0); + + for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + JSTaggedValue dvalue = TaggedTreeMap::Delete(thread, tmap, TaggedTreeMap::FindEntry(thread, tmap, key)); + EXPECT_EQ(dvalue, tmap.GetTaggedValue()); + } + EXPECT_EQ(tmap->NumberOfElements(), NODE_NUMBERS / 2); + EXPECT_EQ(tmap->NumberOfDeletedElements(), NODE_NUMBERS / 2); + + std::string myKey("mykey"); + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + } + EXPECT_EQ(tmap->NumberOfElements(), NODE_NUMBERS + NODE_NUMBERS / 2); + EXPECT_EQ(tmap->NumberOfDeletedElements(), 0); + + for (uint32_t i = NODE_NUMBERS / 2; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + JSTaggedValue gvalue = TaggedTreeMap::Get(thread, tmap, key); + EXPECT_EQ(gvalue, value.GetTaggedValue()); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSTaggedValue gvalue = TaggedTreeMap::Get(thread, tmap, key); + EXPECT_EQ(gvalue, value.GetTaggedValue()); + } + EXPECT_EQ(tmap->NumberOfElements(), NODE_NUMBERS + NODE_NUMBERS / 2); + EXPECT_EQ(tmap->Capacity(), 31); // 31 means capacity after grow + + // TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + EXPECT_EQ(tset->NumberOfElements(), NODE_NUMBERS); + EXPECT_EQ(tset->NumberOfDeletedElements(), 0); + + for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) { + key.Update(JSTaggedValue(i)); + JSTaggedValue dvalue = TaggedTreeSet::Delete(thread, tset, TaggedTreeSet::FindEntry(thread, tset, key)); + EXPECT_EQ(dvalue, tset.GetTaggedValue()); + } + EXPECT_EQ(tset->NumberOfElements(), NODE_NUMBERS / 2); + EXPECT_EQ(tset->NumberOfDeletedElements(), NODE_NUMBERS / 2); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + } + EXPECT_EQ(tset->NumberOfElements(), NODE_NUMBERS + NODE_NUMBERS / 2); + EXPECT_EQ(tset->NumberOfDeletedElements(), 0); + + for (uint32_t i = NODE_NUMBERS / 2; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + int entry = TaggedTreeSet::FindEntry(thread, tset, key); + EXPECT_TRUE(entry >= 0); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + int entry = TaggedTreeSet::FindEntry(thread, tset, key); + EXPECT_TRUE(entry >= 0); + } + EXPECT_EQ(tset->NumberOfElements(), NODE_NUMBERS + NODE_NUMBERS / 2); + EXPECT_EQ(tset->Capacity(), 31); // 31 means capacity after grow +} + +HWTEST_F_L0(TaggedTreeTest, RBTreeAddCheck) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + constexpr uint32_t NODE_NUMBERS = 16; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + std::string myValue("myvalue"); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + } + EXPECT_TRUE(tmap->NumberOfElements() == NODE_NUMBERS * 2); + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + } + EXPECT_TRUE(tset->NumberOfElements() == NODE_NUMBERS * 2); +} + +HWTEST_F_L0(TaggedTreeTest, RBTreeDeleteCheck) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + constexpr uint32_t NODE_NUMBERS = 16; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + std::string myValue("myvalue"); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + } + + JSMutableHandle resOfDelete(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + resOfDelete.Update(TaggedTreeMap::Delete(thread, tmap, TaggedTreeMap::FindEntry(thread, tmap, key))); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + EXPECT_EQ(resOfDelete.GetTaggedValue(), tmap.GetTaggedValue()); + } + EXPECT_TRUE(tmap->NumberOfElements() == NODE_NUMBERS); + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + resOfDelete.Update(TaggedTreeSet::Delete(thread, tset, TaggedTreeSet::FindEntry(thread, tset, key))); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + EXPECT_EQ(resOfDelete.GetTaggedValue(), tset.GetTaggedValue()); + } + EXPECT_TRUE(tset->NumberOfElements() == NODE_NUMBERS); +} + +HWTEST_F_L0(TaggedTreeTest, CustomCompareFunctionTest) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + constexpr uint32_t NODE_NUMBERS = 9; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + std::string myValue("myvalue"); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestCompareFunction)); + tmap->SetCompare(thread, func.GetTaggedValue()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + std::string ivalue = myValue + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + } + EXPECT_TRUE(tmap->NumberOfElements() == NODE_NUMBERS * 2); + + JSHandle arr = TaggedTreeMap::GetArrayFromMap(thread, tmap); + EXPECT_EQ(arr->GetLength(), NODE_NUMBERS * 2); + for (uint32_t i = NODE_NUMBERS; i < NODE_NUMBERS * 2; i++) { + EXPECT_EQ(tmap->GetKey(arr->Get(i).GetInt()).GetInt(), (NODE_NUMBERS * 2 - 1 - i)); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(NODE_NUMBERS - 1 - i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + EXPECT_EQ(tmap->GetKey(arr->Get(i).GetInt()), key.GetTaggedValue()); + } + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + tset->SetCompare(thread, func.GetTaggedValue()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + } + EXPECT_TRUE(tset->NumberOfElements() == NODE_NUMBERS * 2); + + JSHandle sarr = TaggedTreeSet::GetArrayFromSet(thread, tset); + EXPECT_EQ(arr->GetLength(), NODE_NUMBERS * 2); + for (uint32_t i = NODE_NUMBERS; i < NODE_NUMBERS * 2; i++) { + EXPECT_EQ(tset->GetKey(sarr->Get(i).GetInt()), JSTaggedValue(NODE_NUMBERS * 2 - 1 - i)); + } + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(NODE_NUMBERS - 1 - i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + EXPECT_EQ(tset->GetKey(sarr->Get(i).GetInt()), key.GetTaggedValue()); + } +} + +HWTEST_F_L0(TaggedTreeTest, RBTreeDeleteShrink) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + constexpr uint32_t NODE_NUMBERS = 32; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + std::string myKey("mykey"); + std::string myValue("myvalue"); + + // test TaggedTreeMap + JSMutableHandle tmap(thread, TaggedTreeMap::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + key.Update(JSTaggedValue(i)); + value.Update(JSTaggedValue(i)); + tmap.Update(TaggedTreeMap::Set(thread, tmap, key, value)); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + } + + JSMutableHandle resOfDelete(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) { + key.Update(JSTaggedValue(i)); + resOfDelete.Update(TaggedTreeMap::Delete(thread, tmap, TaggedTreeMap::FindEntry(thread, tmap, key))); + bool success = IsVaildRBTree(thread, tmap, tmap->GetRootEntries()); + EXPECT_TRUE(success); + EXPECT_EQ(resOfDelete.GetTaggedValue(), tmap.GetTaggedValue()); + } + + { + EXPECT_EQ(tmap->Capacity(), NODE_NUMBERS * 2 - 1); + key.Update(JSTaggedValue(NODE_NUMBERS / 2)); + resOfDelete.Update(TaggedTreeMap::Delete(thread, tmap, TaggedTreeMap::FindEntry(thread, tmap, key))); + EXPECT_NE(resOfDelete.GetTaggedValue(), tmap.GetTaggedValue()); + + key.Update(JSTaggedValue(NODE_NUMBERS / 2 + 1)); + JSHandle newMap = JSHandle::Cast(resOfDelete); + resOfDelete.Update(TaggedTreeMap::Delete(thread, newMap, TaggedTreeMap::FindEntry(thread, newMap, key))); + EXPECT_EQ(tmap->NumberOfElements(), NODE_NUMBERS / 2 - 1); + EXPECT_EQ(newMap->NumberOfElements(), NODE_NUMBERS / 2 - 2); // 2 means two elements + bool success = IsVaildRBTree(thread, newMap, newMap->GetRootEntries()); + EXPECT_TRUE(success); + EXPECT_EQ(newMap->Capacity(), NODE_NUMBERS - 1); + } + + // test TaggedTreeSet + JSMutableHandle tset(thread, TaggedTreeSet::Create(thread)); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + tset.Update(TaggedTreeSet::Add(thread, tset, key)); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + } + + for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) { + std::string ikey = myKey + std::to_string(i); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + resOfDelete.Update(TaggedTreeSet::Delete(thread, tset, TaggedTreeSet::FindEntry(thread, tset, key))); + bool success = IsVaildRBTree(thread, tset, tset->GetRootEntries()); + EXPECT_TRUE(success); + EXPECT_EQ(resOfDelete.GetTaggedValue(), tset.GetTaggedValue()); + } + + { + EXPECT_EQ(tset->Capacity(), NODE_NUMBERS * 2 - 1); + std::string ikey = myKey + std::to_string(NODE_NUMBERS / 2); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + resOfDelete.Update(TaggedTreeSet::Delete(thread, tset, TaggedTreeSet::FindEntry(thread, tset, key))); + EXPECT_NE(resOfDelete.GetTaggedValue(), tset.GetTaggedValue()); + + ikey = myKey + std::to_string(NODE_NUMBERS / 2 + 1); + key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); + JSHandle newSet = JSHandle::Cast(resOfDelete); + resOfDelete.Update(TaggedTreeSet::Delete(thread, newSet, TaggedTreeSet::FindEntry(thread, newSet, key))); + EXPECT_EQ(tset->NumberOfElements(), NODE_NUMBERS / 2 - 1); + EXPECT_EQ(newSet->NumberOfElements(), NODE_NUMBERS / 2 - 2); + bool success = IsVaildRBTree(thread, newSet, newSet->GetRootEntries()); + EXPECT_TRUE(success); + EXPECT_EQ(newSet->Capacity(), NODE_NUMBERS - 1); + } +} +} // namespace panda::test diff --git a/test/resource/js_runtime/ohos_test.xml b/test/resource/js_runtime/ohos_test.xml index 88ab552d36..90887454d2 100644 --- a/test/resource/js_runtime/ohos_test.xml +++ b/test/resource/js_runtime/ohos_test.xml @@ -377,6 +377,11 @@