/* * 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/containers/containers_private.h" #include "containers_arraylist.h" #include "containers_treemap.h" #include "containers_treeset.h" #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.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_api_arraylist.h" #include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_function.h" namespace panda::ecmascript::containers { JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) { ASSERT(msg); JSThread *thread = msg->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle argv = GetCallArg(msg, 0); JSHandle thisValue(GetThis(msg)); uint32_t tag = 0; if (!JSTaggedValue::ToElementIndex(argv.GetTaggedValue(), &tag) || tag >= ContainerTag::END) { THROW_TYPE_ERROR_AND_RETURN(thread, "Incorrect input parameters", JSTaggedValue::Exception()); } JSTaggedValue res = JSTaggedValue::Undefined(); switch (tag) { case ContainerTag::ArrayList: { res = InitializeContainer(thread, thisValue, InitializeArrayList, "ArrayListConstructor"); break; } case ContainerTag::TreeMap: { res = InitializeContainer(thread, thisValue, InitializeTreeMap, "TreeMapConstructor"); break; } case ContainerTag::TreeSet: { res = InitializeContainer(thread, thisValue, InitializeTreeSet, "TreeSetConstructor"); break; } case ContainerTag::Queue: case ContainerTag::Deque: case ContainerTag::Stack: case ContainerTag::Vector: case ContainerTag::List: case ContainerTag::LinkedList: case ContainerTag::HashMap: case ContainerTag::HashSet: case ContainerTag::LightWeightMap: case ContainerTag::LightWeightSet: case ContainerTag::PlainArray: case ContainerTag::END: break; default: UNREACHABLE(); } return res; } JSTaggedValue ContainersPrivate::InitializeContainer(JSThread *thread, const JSHandle &obj, InitializeFunction func, const char *name) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle key(factory->NewFromCanBeCompressString(name)); JSTaggedValue value = FastRuntimeStub::GetPropertyByName(thread, obj.GetTaggedValue(), key.GetTaggedValue()); if (value != JSTaggedValue::Undefined()) { return value; } JSHandle map = func(thread); SetFrozenConstructor(thread, obj, name, map); return map.GetTaggedValue(); } JSHandle ContainersPrivate::NewContainerConstructor(JSThread *thread, const JSHandle &prototype, EcmaEntrypoint ctorFunc, const char *name, int length) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle ctor = factory->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSFunction::SetFunctionLength(thread, ctor, JSTaggedValue(length)); JSHandle nameString(factory->NewFromCanBeCompressString(name)); JSFunction::SetFunctionName(thread, JSHandle(ctor), nameString, globalConst->GetHandledUndefined()); JSHandle constructorKey = globalConst->GetHandledConstructorString(); PropertyDescriptor descriptor1(thread, JSHandle::Cast(ctor), true, false, true); JSObject::DefineOwnProperty(thread, prototype, constructorKey, descriptor1); /* set "prototype" in constructor */ ctor->SetFunctionPrototype(thread, prototype.GetTaggedValue()); return ctor; } void ContainersPrivate::SetFrozenFunction(JSThread *thread, const JSHandle &obj, const char *key, EcmaEntrypoint func, int length) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle keyString(factory->NewFromCanBeCompressString(key)); JSHandle function = NewFunction(thread, keyString, func, length); PropertyDescriptor descriptor(thread, JSHandle(function), false, false, false); JSObject::DefineOwnProperty(thread, obj, keyString, descriptor); } void ContainersPrivate::SetFrozenConstructor(JSThread *thread, const JSHandle &obj, const char *keyChar, JSHandle &value) { JSObject::PreventExtensions(thread, JSHandle::Cast(value)); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle key(factory->NewFromCanBeCompressString(keyChar)); PropertyDescriptor descriptor(thread, value, false, false, false); JSObject::DefineOwnProperty(thread, obj, key, descriptor); } JSHandle ContainersPrivate::NewFunction(JSThread *thread, const JSHandle &key, EcmaEntrypoint func, int length) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle function = factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(func)); JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); JSHandle baseFunction(function); JSFunction::SetFunctionName(thread, baseFunction, key, thread->GlobalConstants()->GetHandledUndefined()); return function; } JSHandle ContainersPrivate::CreateGetter(JSThread *thread, EcmaEntrypoint func, const char *name, int length) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle function = factory->NewJSFunction(env, reinterpret_cast(func)); JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); JSHandle funcName(factory->NewFromString(name)); JSHandle prefix = thread->GlobalConstants()->GetHandledGetString(); JSFunction::SetFunctionName(thread, JSHandle(function), funcName, prefix); return JSHandle(function); } void ContainersPrivate::SetGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &getter) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle accessor = factory->NewAccessorData(); accessor->SetGetter(thread, getter); PropertyAttributes attr = PropertyAttributes::DefaultAccessor(false, false, false); JSObject::AddAccessor(thread, JSHandle::Cast(obj), key, accessor, attr); } void ContainersPrivate::SetFunctionAtSymbol(JSThread *thread, const JSHandle &env, const JSHandle &obj, const JSHandle &symbol, const char *name, EcmaEntrypoint func, int length) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle function = factory->NewJSFunction(env, reinterpret_cast(func)); JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); JSHandle nameString(factory->NewFromString(name)); JSHandle baseFunction(function); JSFunction::SetFunctionName(thread, baseFunction, nameString, thread->GlobalConstants()->GetHandledUndefined()); PropertyDescriptor descriptor(thread, JSHandle::Cast(function), false, false, false); JSObject::DefineOwnProperty(thread, obj, symbol, descriptor); } void ContainersPrivate::SetStringTagSymbol(JSThread *thread, const JSHandle &env, const JSHandle &obj, const char *key) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle tag(factory->NewFromString(key)); JSHandle symbol = env->GetToStringTagSymbol(); PropertyDescriptor desc(thread, tag, false, false, false); JSObject::DefineOwnProperty(thread, obj, symbol, desc); } JSHandle ContainersPrivate::InitializeArrayList(JSThread *thread) { auto globalConst = const_cast(thread->GlobalConstants()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // ArrayList.prototype JSHandle prototype = factory->NewEmptyJSObject(); JSHandle arrayListFuncPrototypeValue(prototype); // ArrayList.prototype_or_dynclass JSHandle arrayListInstanceDynclass = factory->NewEcmaDynClass(JSAPIArrayList::SIZE, JSType::JS_API_ARRAY_LIST, arrayListFuncPrototypeValue); // ArrayList() = new Function() JSHandle arrayListFunction(NewContainerConstructor( thread, prototype, ContainersArrayList::ArrayListConstructor, "ArrayList", FuncLength::ZERO)); JSHandle(arrayListFunction)->SetFunctionPrototype(thread, arrayListInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread, JSHandle(prototype), constructorKey, arrayListFunction); // ArrayList.prototype SetFrozenFunction(thread, prototype, "add", ContainersArrayList::Add, FuncLength::ONE); SetFrozenFunction(thread, prototype, "insert", ContainersArrayList::Insert, FuncLength::TWO); SetFrozenFunction(thread, prototype, "clear", ContainersArrayList::Clear, FuncLength::ZERO); SetFrozenFunction(thread, prototype, "clone", ContainersArrayList::Clone, FuncLength::ZERO); SetFrozenFunction(thread, prototype, "has", ContainersArrayList::Has, FuncLength::ONE); SetFrozenFunction(thread, prototype, "getCapacity", ContainersArrayList::GetCapacity, FuncLength::ZERO); SetFrozenFunction(thread, prototype, "increaseCapacityTo", ContainersArrayList::IncreaseCapacityTo, FuncLength::ONE); SetFrozenFunction(thread, prototype, "trimToCurrentLength", ContainersArrayList::TrimToCurrentLength, FuncLength::ZERO); SetFrozenFunction(thread, prototype, "getIndexOf", ContainersArrayList::GetIndexOf, FuncLength::ONE); SetFrozenFunction(thread, prototype, "isEmpty", ContainersArrayList::IsEmpty, FuncLength::ZERO); SetFrozenFunction(thread, prototype, "getLastIndexOf", ContainersArrayList::GetLastIndexOf, FuncLength::ONE); SetFrozenFunction(thread, prototype, "removeByIndex", ContainersArrayList::RemoveByIndex, FuncLength::ONE); SetFrozenFunction(thread, prototype, "remove", ContainersArrayList::Remove, FuncLength::ONE); SetFrozenFunction(thread, prototype, "removeByRange", ContainersArrayList::RemoveByRange, FuncLength::TWO); SetFrozenFunction(thread, prototype, "replaceAllElements", ContainersArrayList::ReplaceAllElements, FuncLength::TWO); SetFrozenFunction(thread, prototype, "sort", ContainersArrayList::Sort, FuncLength::ONE); SetFrozenFunction(thread, prototype, "subArrayList", ContainersArrayList::SubArrayList, FuncLength::TWO); SetFrozenFunction(thread, prototype, "convertToArray", ContainersArrayList::ConvertToArray, FuncLength::ZERO); SetFrozenFunction(thread, prototype, "forEach", ContainersArrayList::ForEach, FuncLength::TWO); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); SetStringTagSymbol(thread, env, prototype, "ArrayList"); JSHandle lengthGetter = CreateGetter(thread, ContainersArrayList::GetSize, "length", FuncLength::ZERO); JSHandle lengthKey(factory->NewFromCanBeCompressString("length")); SetGetter(thread, prototype, lengthKey, lengthGetter); SetFunctionAtSymbol(thread, env, prototype, env->GetIteratorSymbol(), "[Symbol.iterator]", ContainersArrayList::GetIteratorObj, FuncLength::ONE); ContainersPrivate::InitializeArrayListIterator(thread, env, globalConst); globalConst->SetConstant(ConstantIndex::ARRAYLIST_FUNCTION_INDEX, arrayListFunction.GetTaggedValue()); return arrayListFunction; } void ContainersPrivate::InitializeArrayListIterator(JSThread *thread, const JSHandle &env, GlobalEnvConstants *globalConst) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // Iterator.dynclass JSHandle iteratorFuncDynclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ITERATOR, env->GetIteratorPrototype()); // ArrayListIterator.prototype JSHandle arrayListIteratorPrototype(factory->NewJSObject(iteratorFuncDynclass)); // Iterator.prototype.next() SetFrozenFunction(thread, arrayListIteratorPrototype, "next", JSAPIArrayListIterator::Next, FuncLength::ONE); SetStringTagSymbol(thread, env, arrayListIteratorPrototype, "ArrayList Iterator"); globalConst->SetConstant(ConstantIndex::ARRAYLIST_ITERATOR_PROTOTYPE_INDEX, arrayListIteratorPrototype.GetTaggedValue()); } 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); SetFrozenFunction(thread, mapFuncPrototype, "isEmpty", ContainersTreeMap::IsEmpty, 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