diff --git a/BUILD.gn b/BUILD.gn index 23f55c0d..5c224869 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -329,8 +329,10 @@ ecma_source = [ "ecmascript/builtins/builtins_weak_map.cpp", "ecmascript/builtins/builtins_weak_set.cpp", "ecmascript/containers/containers_arraylist.cpp", + "ecmascript/containers/containers_deque.cpp", "ecmascript/containers/containers_private.cpp", "ecmascript/containers/containers_queue.cpp", + "ecmascript/containers/containers_stack.cpp", "ecmascript/containers/containers_treemap.cpp", "ecmascript/containers/containers_treeset.cpp", "ecmascript/dfx/vmstat/caller_stat.cpp", @@ -360,8 +362,12 @@ ecma_source = [ "ecmascript/jobs/micro_job_queue.cpp", "ecmascript/jspandafile/js_pandafile.cpp", "ecmascript/jspandafile/js_pandafile_manager.cpp", + "ecmascript/js_api_deque.cpp", + "ecmascript/js_api_deque_iterator.cpp", "ecmascript/js_api_queue.cpp", "ecmascript/js_api_queue_iterator.cpp", + "ecmascript/js_api_stack.cpp", + "ecmascript/js_api_stack_iterator.cpp", "ecmascript/jspandafile/class_info_extractor.cpp", "ecmascript/jspandafile/ecma_class_linker_extension.cpp", "ecmascript/jspandafile/literal_data_extractor.cpp", diff --git a/ecmascript/builtins.cpp b/ecmascript/builtins.cpp index fc5aec97..78190a59 100644 --- a/ecmascript/builtins.cpp +++ b/ecmascript/builtins.cpp @@ -66,10 +66,8 @@ #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/containers/containers_private.h" #include "ecmascript/ecma_runtime_call_info.h" -#include "ecmascript/js_api_queue.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraybuffer.h" -#include "ecmascript/js_api_arraylist.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_async_function.h" #include "ecmascript/js_collator.h" @@ -1519,6 +1517,9 @@ void Builtins::InitializeIterator(const JSHandle &env, const JSHandle JSHandle iteratorFuncDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ITERATOR, JSHandle(iteratorPrototype)); + auto globalConst = const_cast(thread_->GlobalConstants()); + globalConst->SetConstant(ConstantIndex::JS_API_ITERATOR_FUNC_DYN_CLASS_INDEX, iteratorFuncDynclass); + InitializeForinIterator(env, iteratorFuncDynclass); InitializeSetIterator(env, iteratorFuncDynclass); InitializeMapIterator(env, iteratorFuncDynclass); diff --git a/ecmascript/containers/containers_deque.cpp b/ecmascript/containers/containers_deque.cpp new file mode 100644 index 00000000..25f7ddb2 --- /dev/null +++ b/ecmascript/containers/containers_deque.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2022 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_deque.h" +#include "ecmascript/base/array_helper.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/internal_call_params.h" +#include "ecmascript/js_api_deque.h" +#include "ecmascript/js_array.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersDeque::DequeConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, 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()); + } + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + JSHandle newElements = factory->NewTaggedArray(JSAPIDeque::DEFAULT_CAPACITY_LENGTH); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + obj->SetElements(thread, newElements); + + return obj.GetTaggedValue(); +} + +JSTaggedValue ContainersDeque::InsertFront(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, InsertFront); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSAPIDeque::InsertFront(thread, JSHandle::Cast(self), value); + + return JSTaggedValue::True(); +} + + +JSTaggedValue ContainersDeque::InsertEnd(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, InsertEnd); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + JSAPIDeque::InsertEnd(thread, JSHandle::Cast(self), value); + + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersDeque::GetFirst(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, GetFirst); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle deque = JSHandle::Cast(self); + JSTaggedValue firstElement = deque->GetFront(); + return firstElement; +} + +JSTaggedValue ContainersDeque::GetLast(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, GetLast); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle deque = JSHandle::Cast(self); + JSTaggedValue lastElement = deque->GetTail(); + return lastElement; +} + +JSTaggedValue ContainersDeque::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + + JSHandle deque = JSHandle::Cast(self); + bool isHas = deque->Has(value.GetTaggedValue()); + return GetTaggedBoolean(isHas); +} + +JSTaggedValue ContainersDeque::PopFirst(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, PopFirst); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle deque = JSHandle::Cast(self); + JSTaggedValue firstElement = deque->PopFirst(); + return firstElement; +} + +JSTaggedValue ContainersDeque::PopLast(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, PopLast); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle deque = JSHandle::Cast(self); + JSTaggedValue lastElement = deque->PopLast(); + return lastElement; +} + +JSTaggedValue ContainersDeque::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisHandle = GetThis(argv); + JSHandle deque = JSHandle::Cast(GetThis(argv)); + if (!thisHandle->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not IsJSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + JSHandle thisArgHandle = GetCallArg(argv, 1); + + InternalCallParams *arguments = thread->GetInternalCallParams(); + uint32_t first = deque->GetFirst(); + uint32_t last = deque->GetLast(); + + JSHandle elements(thread, deque->GetElements()); + uint32_t capacity = elements->GetLength(); + + uint32_t index = 0; + while (first != last) { + JSTaggedValue kValue = deque->Get(index); + arguments->MakeArgv(kValue, JSTaggedValue(index), thisHandle.GetTaggedValue()); + JSTaggedValue funcResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + ASSERT(capacity != 0); + first = (first + 1) % capacity; + index = index + 1; + } + + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersDeque::GetIteratorObj(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, GetIteratorObj); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSTaggedValue values = JSAPIDeque::GetIteratorObj(thread, JSHandle::Cast(self)); + + return values; +} + +JSTaggedValue ContainersDeque::GetSize(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Deque, GetSize); + JSThread *thread = argv->GetThread(); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIDeque()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIDeque", JSTaggedValue::Exception()); + } + + JSHandle deque = JSHandle::Cast(self); + uint32_t length = deque->GetSize(); + + return JSTaggedValue(length); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_deque.h b/ecmascript/containers/containers_deque.h new file mode 100644 index 00000000..2916c614 --- /dev/null +++ b/ecmascript/containers/containers_deque.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 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_DEQUE_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_DEQUE_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +class ContainersDeque : public base::BuiltinsBase { +public: + static JSTaggedValue DequeConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue InsertFront(EcmaRuntimeCallInfo *argv); + static JSTaggedValue InsertEnd(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLast(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + static JSTaggedValue PopFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue PopLast(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetIteratorObj(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_DEQUE_H diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp index 4d596b71..08d72d11 100644 --- a/ecmascript/containers/containers_private.cpp +++ b/ecmascript/containers/containers_private.cpp @@ -16,14 +16,20 @@ #include "ecmascript/containers/containers_private.h" #include "containers_arraylist.h" +#include "containers_deque.h" #include "containers_queue.h" +#include "containers_stack.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_deque.h" +#include "ecmascript/js_api_deque_iterator.h" #include "ecmascript/js_api_queue.h" #include "ecmascript/js_api_queue_iterator.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" #include "ecmascript/js_api_tree_map.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set.h" @@ -60,12 +66,18 @@ JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) res = InitializeContainer(thread, thisValue, InitializeTreeSet, "TreeSetConstructor"); break; } + case ContainerTag::Stack: { + res = InitializeContainer(thread, thisValue, InitializeStack, "StackConstructor"); + break; + } case ContainerTag::Queue: { res = InitializeContainer(thread, thisValue, InitializeQueue, "QueueConstructor"); break; } - case ContainerTag::Deque: - case ContainerTag::Stack: + case ContainerTag::Deque: { + res = InitializeContainer(thread, thisValue, InitializeDeque, "DequeConstructor"); + break; + } case ContainerTag::Vector: case ContainerTag::List: case ContainerTag::LinkedList: @@ -421,6 +433,64 @@ void ContainersPrivate::InitializeTreeSetIterator(JSThread *thread) globalConst->SetConstant(ConstantIndex::TREESET_ITERATOR_PROTOTYPE_INDEX, setIteratorPrototype.GetTaggedValue()); } +JSHandle ContainersPrivate::InitializeStack(JSThread *thread) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Stack.prototype + JSHandle stackFuncPrototype = factory->NewEmptyJSObject(); + JSHandle stackFuncPrototypeValue(stackFuncPrototype); + // Stack.prototype_or_dynclass + JSHandle stackInstanceDynclass = + factory->NewEcmaDynClass(JSAPIStack::SIZE, JSType::JS_API_STACK, stackFuncPrototypeValue); + // Stack() = new Function() + JSHandle stackFunction(NewContainerConstructor( + thread, stackFuncPrototype, ContainersStack::StackConstructor, "Stack", FuncLength::ZERO)); + JSHandle(stackFunction)->SetFunctionPrototype(thread, stackInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(stackFuncPrototype), constructorKey, stackFunction); + + // Stack.prototype.push() + SetFrozenFunction(thread, stackFuncPrototype, "push", ContainersStack::Push, FuncLength::ONE); + // Stack.prototype.empty() + SetFrozenFunction(thread, stackFuncPrototype, "isEmpty", ContainersStack::IsEmpty, FuncLength::ONE); + // Stack.prototype.peek() + SetFrozenFunction(thread, stackFuncPrototype, "peek", ContainersStack::Peek, FuncLength::ONE); + // Stack.prototype.pop() + SetFrozenFunction(thread, stackFuncPrototype, "pop", ContainersStack::Pop, FuncLength::ONE); + // Stack.prototype.search() + SetFrozenFunction(thread, stackFuncPrototype, "locate", ContainersStack::Locate, FuncLength::ONE); + // Stack.prototype.forEach() + SetFrozenFunction(thread, stackFuncPrototype, "forEach", ContainersStack::ForEach, FuncLength::ONE); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + SetStringTagSymbol(thread, env, stackFuncPrototype, "Stack"); + + JSHandle lengthGetter = CreateGetter(thread, ContainersStack::GetLength, "length", FuncLength::ZERO); + JSHandle lengthKey(factory->NewFromCanBeCompressString("length")); + SetGetter(thread, stackFuncPrototype, lengthKey, lengthGetter); + + SetFunctionAtSymbol(thread, env, stackFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + ContainersStack::Iterator, FuncLength::ONE); + + ContainersPrivate::InitializeStackIterator(thread, globalConst); + return stackFunction; +} + +void ContainersPrivate::InitializeStackIterator(JSThread *thread, GlobalEnvConstants *globalConst) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iteratorFuncDynclass = JSHandle(thread, globalConst-> + GetHandledJSAPIIteratorFuncDynClass().GetObject()); + // StackIterator.prototype + JSHandle stackIteratorPrototype(factory->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFrozenFunction(thread, stackIteratorPrototype, "next", JSAPIStackIterator::Next, FuncLength::ONE); + globalConst->SetConstant(ConstantIndex::STACK_ITERATOR_PROTOTYPE_INDEX, stackIteratorPrototype.GetTaggedValue()); +} + JSHandle ContainersPrivate::InitializeQueue(JSThread *thread) { auto globalConst = const_cast(thread->GlobalConstants()); @@ -473,4 +543,59 @@ void ContainersPrivate::InitializeQueueIterator(JSThread *thread, const JSHandle SetStringTagSymbol(thread, env, queueIteratorPrototype, "Queue Iterator"); globalConst->SetConstant(ConstantIndex::QUEUE_ITERATOR_PROTOTYPE_INDEX, queueIteratorPrototype.GetTaggedValue()); } + +JSHandle ContainersPrivate::InitializeDeque(JSThread *thread) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Deque.prototype + JSHandle dequeFuncPrototype = factory->NewEmptyJSObject(); + JSHandle dequeFuncPrototypeValue(dequeFuncPrototype); + // Deque.prototype_or_dynclass + JSHandle dequeInstanceDynclass = + factory->NewEcmaDynClass(JSAPIDeque::SIZE, JSType::JS_API_DEQUE, dequeFuncPrototypeValue); + // Deque() = new Function() + JSHandle dequeFunction(NewContainerConstructor( + thread, dequeFuncPrototype, ContainersDeque::DequeConstructor, "Deque", FuncLength::ZERO)); + JSHandle(dequeFunction)->SetFunctionPrototype(thread, dequeInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(dequeFuncPrototype), constructorKey, dequeFunction); + + SetFrozenFunction(thread, dequeFuncPrototype, "insertFront", ContainersDeque::InsertFront, FuncLength::ONE); + SetFrozenFunction(thread, dequeFuncPrototype, "insertEnd", ContainersDeque::InsertEnd, FuncLength::ONE); + SetFrozenFunction(thread, dequeFuncPrototype, "getFirst", ContainersDeque::GetFirst, FuncLength::ZERO); + SetFrozenFunction(thread, dequeFuncPrototype, "getLast", ContainersDeque::GetLast, FuncLength::ZERO); + SetFrozenFunction(thread, dequeFuncPrototype, "has", ContainersDeque::Has, FuncLength::ONE); + SetFrozenFunction(thread, dequeFuncPrototype, "popFirst", ContainersDeque::PopFirst, FuncLength::ZERO); + SetFrozenFunction(thread, dequeFuncPrototype, "popLast", ContainersDeque::PopLast, FuncLength::ZERO); + SetFrozenFunction(thread, dequeFuncPrototype, "forEach", ContainersDeque::ForEach, FuncLength::TWO); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + SetStringTagSymbol(thread, env, dequeFuncPrototype, "Deque"); + + JSHandle lengthGetter = CreateGetter(thread, ContainersDeque::GetSize, "length", FuncLength::ZERO); + JSHandle lengthKey(factory->NewFromCanBeCompressString("length")); + SetGetter(thread, dequeFuncPrototype, lengthKey, lengthGetter); + + SetFunctionAtSymbol(thread, env, dequeFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + ContainersDeque::GetIteratorObj, FuncLength::ONE); + + ContainersPrivate::InitializeDequeIterator(thread, env, globalConst); + + return dequeFunction; +} + +void ContainersPrivate::InitializeDequeIterator(JSThread *thread, const JSHandle &env, + GlobalEnvConstants *globalConst) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iteratorFuncDynclass = JSHandle(thread, globalConst-> + GetHandledJSAPIIteratorFuncDynClass().GetObject()); + JSHandle dequeIteratorPrototype(factory->NewJSObject(iteratorFuncDynclass)); + SetFrozenFunction(thread, dequeIteratorPrototype, "next", JSAPIDequeIterator::Next, FuncLength::ONE); + SetStringTagSymbol(thread, env, dequeIteratorPrototype, "Deque Iterator"); + globalConst->SetConstant(ConstantIndex::DEQUE_ITERATOR_PROTOTYPE_INDEX, dequeIteratorPrototype.GetTaggedValue()); +} } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_private.h b/ecmascript/containers/containers_private.h index 866d7f99..33c207f4 100644 --- a/ecmascript/containers/containers_private.h +++ b/ecmascript/containers/containers_private.h @@ -77,6 +77,11 @@ private: static JSHandle InitializeQueue(JSThread *thread); static void InitializeQueueIterator(JSThread *thread, const JSHandle &env, GlobalEnvConstants *globalConst); + static JSHandle InitializeDeque(JSThread *thread); + static void InitializeDequeIterator(JSThread *thread, const JSHandle &env, + GlobalEnvConstants *globalConst); + static JSHandle InitializeStack(JSThread *thread); + static void InitializeStackIterator(JSThread *thread, GlobalEnvConstants *globalConst); }; } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_stack.cpp b/ecmascript/containers/containers_stack.cpp new file mode 100644 index 00000000..4127b329 --- /dev/null +++ b/ecmascript/containers/containers_stack.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2022 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_stack.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/internal_call_params.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersStack::StackConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, 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()); + } + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle stack = JSHandle::Cast(obj); + stack->SetTop(-1); + + return obj.GetTaggedValue(); +} + +JSTaggedValue ContainersStack::IsEmpty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, IsEmpty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + JSHandle stack = JSHandle::Cast(self); + bool judge = stack->Empty(); + + return GetTaggedBoolean(judge); +} + +JSTaggedValue ContainersStack::Push(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, Push); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSTaggedValue jsValue = JSAPIStack::Push(thread, JSHandle::Cast(self), value); + return jsValue; +} + +JSTaggedValue ContainersStack::Peek(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, Peek); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + + JSHandle stack = JSHandle::Cast(self); + JSTaggedValue jsValue = stack->Peek(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return jsValue; +} + +JSTaggedValue ContainersStack::Locate(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, Locate); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSHandle stack = JSHandle::Cast(self); + int num = stack->Search(value); + return JSTaggedValue(num); +} + +JSTaggedValue ContainersStack::Pop(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, Pop); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + + JSHandle stack = JSHandle::Cast(self); + JSTaggedValue jsValue = stack->Pop(); + return jsValue; +} + +JSTaggedValue ContainersStack::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisHandle = GetThis(argv); + JSHandle stack = JSHandle::Cast(GetThis(argv)); + if (!thisHandle->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + + int len = stack->GetSize(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + JSHandle thisArgHandle = GetCallArg(argv, 1); + InternalCallParams *arguments = thread->GetInternalCallParams(); + + int k = 0; + while (k < len + 1) { + JSTaggedValue kValue = stack->Get(k); + arguments->MakeArgv(kValue, JSTaggedValue(k), thisHandle.GetTaggedValue()); + JSTaggedValue funcResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + k++; + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersStack::Iterator(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, Iterator); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + JSHandle iter(factory->NewJSAPIStackIterator(JSHandle::Cast(self))); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersStack::GetLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + BUILTINS_API_TRACE(argv->GetThread(), Stack, GetLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); // JSAPIStack + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIStack()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIStack", JSTaggedValue::Exception()); + } + + int len = (JSHandle::Cast(thisHandle))->GetSize(); + return JSTaggedValue(len + 1); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_stack.h b/ecmascript/containers/containers_stack.h new file mode 100644 index 00000000..e16d0292 --- /dev/null +++ b/ecmascript/containers/containers_stack.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 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_STACK_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_STACK_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +class ContainersStack : public base::BuiltinsBase { +public: + static JSTaggedValue StackConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Iterator(EcmaRuntimeCallInfo *argv); + static JSTaggedValue IsEmpty(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Push(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Peek(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Pop(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Locate(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_STACK_H diff --git a/ecmascript/containers/tests/BUILD.gn b/ecmascript/containers/tests/BUILD.gn index 0a36a89f..ec995478 100644 --- a/ecmascript/containers/tests/BUILD.gn +++ b/ecmascript/containers/tests/BUILD.gn @@ -59,11 +59,67 @@ host_unittest_action("ContainersTreeSetTest") { } } +host_unittest_action("ContainersStackTest") { + module_out_path = module_output_path + + sources = [ + # test file + "containers_stack_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("ContainersDequeTest") { + module_out_path = module_output_path + + sources = [ + # test file + "containers_deque_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" ] + } +} + group("unittest") { testonly = true # deps file deps = [ + ":ContainersDequeTest", + ":ContainersStackTest", ":ContainersTreeMapTest", ":ContainersTreeSetTest", ] @@ -74,6 +130,8 @@ group("host_unittest") { # deps file deps = [ + ":ContainersDequeTestAction", + ":ContainersStackTestAction", ":ContainersTreeMapTestAction", ":ContainersTreeSetTestAction", ] diff --git a/ecmascript/containers/tests/containers_deque_test.cpp b/ecmascript/containers/tests/containers_deque_test.cpp new file mode 100644 index 00000000..16a0fcb3 --- /dev/null +++ b/ecmascript/containers/tests/containers_deque_test.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2022 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_deque.h" +#include "ecmascript/containers/containers_private.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::containers; + +namespace panda::test { +class ContainersDequeTest : 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}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSHandle value = GetCallArg(argv, 0); + JSHandle key = GetCallArg(argv, 1); + JSHandle deque = GetCallArg(argv, 2); // 2 means the secode arg + if (!deque->IsUndefined()) { + if (value->IsNumber()) { + TaggedArray *elements = TaggedArray::Cast(JSAPIDeque::Cast(deque.GetTaggedValue(). + GetTaggedObject())->GetElements().GetTaggedObject()); + JSTaggedValue result = elements->Get(key->GetInt()); + EXPECT_EQ(result, value.GetTaggedValue()); + } + } + return JSTaggedValue::True(); + } + }; +protected: + JSTaggedValue InitializeDequeConstructor() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromCanBeCompressString("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(ContainerTag::Deque))); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersPrivate::Load(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + return result; + } + + JSHandle CreateJSAPIDeque(JSTaggedValue compare = JSTaggedValue::Undefined()) + { + JSHandle compareHandle(thread, compare); + JSHandle newTarget(thread, InitializeDequeConstructor()); + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(newTarget.GetTaggedValue()); + objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersDeque::DequeConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle deque(thread, result); + return deque; + } +}; + +HWTEST_F_L0(ContainersDequeTest, DequeConstructor) +{ + InitializeDequeConstructor(); + JSHandle newTarget(thread, InitializeDequeConstructor()); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + objCallInfo->SetFunction(newTarget.GetTaggedValue()); + objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersDeque::DequeConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsJSAPIDeque()); + JSHandle deque(thread, result); + JSTaggedValue resultProto = JSHandle::Cast(deque)->GetPrototype(thread); + JSTaggedValue funcProto = newTarget->GetFunctionPrototype(); + ASSERT_EQ(resultProto, funcProto); +} + +HWTEST_F_L0(ContainersDequeTest, InsertFrontAndGetFirst) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle deque = CreateJSAPIDeque(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersDeque::InsertFront(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(ContainersDeque::GetFirst(callInfo.get()), JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersDequeTest, InsertEndAndGetLast) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle deque = CreateJSAPIDeque(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersDeque::InsertEnd(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(ContainersDeque::GetLast(callInfo.get()), JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersDequeTest, Has) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle deque = CreateJSAPIDeque(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersDeque::InsertEnd(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } + + int num = 7; + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersDeque::Has(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + } + num = 7; + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i + 8)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersDeque::Has(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::False()); + } +} + +HWTEST_F_L0(ContainersDequeTest, PopFirst) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle deque = CreateJSAPIDeque(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersDeque::InsertFront(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(ContainersDeque::PopFirst(callInfo.get()), JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersDequeTest, PopLast) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle deque = CreateJSAPIDeque(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersDeque::InsertEnd(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(ContainersDeque::PopLast(callInfo.get()), JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersDequeTest, ForEach) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle deque = CreateJSAPIDeque(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersDeque::InsertEnd(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dlist = CreateJSAPIDeque(); + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForEachFunc)); + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(deque.GetTaggedValue()); + callInfo->SetCallArg(0, func.GetTaggedValue()); + callInfo->SetCallArg(1, dlist.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersDeque::ForEach(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } +} +} // namespace panda::test diff --git a/ecmascript/containers/tests/containers_stack_test.cpp b/ecmascript/containers/tests/containers_stack_test.cpp new file mode 100644 index 00000000..1892109b --- /dev/null +++ b/ecmascript/containers/tests/containers_stack_test.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2022 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_stack.h" +#include "ecmascript/containers/containers_private.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::containers; + +namespace panda::test { +class ContainersStackTest : 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}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSHandle value = GetCallArg(argv, 0); + JSHandle key = GetCallArg(argv, 1); + JSHandle stack = GetCallArg(argv, 2); // 2 means the secode arg + if (!stack->IsUndefined()) { + if (value->IsNumber()) { + TaggedArray *elements = TaggedArray::Cast(JSAPIStack::Cast(stack.GetTaggedValue(). + GetTaggedObject())->GetElements().GetTaggedObject()); + JSTaggedValue result = elements->Get(key->GetInt()); + EXPECT_EQ(result, value.GetTaggedValue()); + } + } + return JSTaggedValue::True(); + } + }; +protected: + JSTaggedValue InitializeStackConstructor() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromCanBeCompressString("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(ContainerTag::Stack))); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersPrivate::Load(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + return result; + } + + JSHandle CreateJSAPIStack(JSTaggedValue compare = JSTaggedValue::Undefined()) + { + JSHandle compareHandle(thread, compare); + JSHandle newTarget(thread, InitializeStackConstructor()); + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(newTarget.GetTaggedValue()); + objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle stack(thread, result); + return stack; + } +}; + +HWTEST_F_L0(ContainersStackTest, StackConstructor) +{ + InitializeStackConstructor(); + JSHandle newTarget(thread, InitializeStackConstructor()); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + objCallInfo->SetFunction(newTarget.GetTaggedValue()); + objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsJSAPIStack()); + JSHandle stack(thread, result); + JSTaggedValue resultProto = JSHandle::Cast(stack)->GetPrototype(thread); + JSTaggedValue funcProto = newTarget->GetFunctionPrototype(); + ASSERT_EQ(resultProto, funcProto); +} + +HWTEST_F_L0(ContainersStackTest, PushAndPeek) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle stack = CreateJSAPIStack(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersStack::Push(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(i)); + EXPECT_EQ(ContainersStack::Peek(callInfo.get()), JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersStackTest, Pop) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle stack = CreateJSAPIStack(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersStack::Push(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } + + int num = 7; + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersStack::Pop(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(num--)); + } +} + +HWTEST_F_L0(ContainersStackTest, IsEmpty) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle stack = CreateJSAPIStack(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersStack::Push(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue result = ContainersStack::IsEmpty(callInfo.get()); + EXPECT_EQ(result, JSTaggedValue::False()); + } + + int num = 7; + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersStack::Pop(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(num--)); + if (num == -1) { + JSTaggedValue consequence = ContainersStack::IsEmpty(callInfo.get()); + EXPECT_EQ(consequence, JSTaggedValue::True()); + } + } +} + +HWTEST_F_L0(ContainersStackTest, Locate) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle stack = CreateJSAPIStack(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersStack::Push(callInfo.get()); + JSTaggedValue result = ContainersStack::Locate(callInfo.get()); + EXPECT_EQ(result, JSTaggedValue(i)); + TestHelper::TearDownFrame(thread, prev); + } +} + +HWTEST_F_L0(ContainersStackTest, ForEach) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle stack = CreateJSAPIStack(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersStack::Push(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dlist = CreateJSAPIStack(); + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForEachFunc)); + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(stack.GetTaggedValue()); + callInfo->SetCallArg(0, func.GetTaggedValue()); + callInfo->SetCallArg(1, dlist.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersStack::ForEach(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } +} +} // namespace panda::test diff --git a/ecmascript/dfx/hprof/heap_snapshot.cpp b/ecmascript/dfx/hprof/heap_snapshot.cpp index 26ad80b3..7bb9a286 100644 --- a/ecmascript/dfx/hprof/heap_snapshot.cpp +++ b/ecmascript/dfx/hprof/heap_snapshot.cpp @@ -342,6 +342,14 @@ CString *HeapSnapShot::GenerateNodeName(TaggedObject *entry) return GetString("Queue"); case JSType::JS_API_QUEUE_ITERATOR: return GetString("QueueIterator"); + case JSType::JS_API_DEQUE: + return GetString("Deque"); + case JSType::JS_API_DEQUE_ITERATOR: + return GetString("DequeIterator"); + case JSType::JS_API_STACK: + return GetString("Stack"); + case JSType::JS_API_STACK_ITERATOR: + return GetString("StackIterator"); case JSType::SOURCE_TEXT_MODULE_RECORD: return GetString("SourceTextModule"); case JSType::IMPORTENTRY_RECORD: diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index 560aad7e..33109552 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -28,8 +28,12 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_deque_iterator.h" #include "ecmascript/js_api_queue.h" #include "ecmascript/js_api_queue_iterator.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" #include "ecmascript/jspandafile/class_info_extractor.h" #include "ecmascript/jspandafile/program_object-inl.h" #include "ecmascript/js_api_tree_map.h" @@ -298,6 +302,14 @@ CString JSHClass::DumpJSType(JSType type) return "Queue"; case JSType::JS_API_QUEUE_ITERATOR: return "QueueIterator"; + case JSType::JS_API_DEQUE: + return "Deque"; + case JSType::JS_API_DEQUE_ITERATOR: + return "DequeIterator"; + case JSType::JS_API_STACK: + return "Stack"; + case JSType::JS_API_STACK_ITERATOR: + return "StackIterator"; default: { CString ret = "unknown type "; return ret + static_cast(type); @@ -682,6 +694,18 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::JS_API_QUEUE_ITERATOR: JSAPIQueueIterator::Cast(obj)->Dump(os); break; + case JSType::JS_API_DEQUE: + JSAPIDeque::Cast(obj)->Dump(os); + break; + case JSType::JS_API_DEQUE_ITERATOR: + JSAPIDequeIterator::Cast(obj)->Dump(os); + break; + case JSType::JS_API_STACK: + JSAPIStack::Cast(obj)->Dump(os); + break; + case JSType::JS_API_STACK_ITERATOR: + JSAPIStackIterator::Cast(obj)->Dump(os); + break; case JSType::SOURCE_TEXT_MODULE_RECORD: SourceTextModule::Cast(obj)->Dump(os); break; @@ -1399,6 +1423,35 @@ void JSAPIArrayListIterator::Dump(std::ostream &os) const JSObject::Dump(os); } +void JSAPIDeque::Dump(std::ostream &os) const +{ + os << " - first: " << std::dec << GetFirst() << "\n"; + os << " - last: " << std::dec << GetLast() << "\n"; + JSObject::Dump(os); +} + +void JSAPIDequeIterator::Dump(std::ostream &os) const +{ + JSAPIDeque *deque = JSAPIDeque::Cast(GetIteratedDeque().GetTaggedObject()); + os << " - length: " << std::dec << deque->GetSize() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex() << "\n"; + JSObject::Dump(os); +} + +void JSAPIStack::Dump(std::ostream &os) const +{ + os << " - top: " << std::dec << GetTop() << "\n"; + JSObject::Dump(os); +} + +void JSAPIStackIterator::Dump(std::ostream &os) const +{ + JSAPIStack *stack = JSAPIStack::Cast(GetIteratedStack().GetTaggedObject()); + os << " - length: " << std::dec << stack->GetSize() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex() << "\n"; + JSObject::Dump(os); +} + void JSArrayIterator::Dump(std::ostream &os) const { JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); @@ -2789,6 +2842,18 @@ static void DumpObject(TaggedObject *obj, case JSType::JS_API_QUEUE_ITERATOR: JSAPIQueueIterator::Cast(obj)->DumpForSnapshot(vec); return; + case JSType::JS_API_DEQUE: + JSAPIDeque::Cast(obj)->DumpForSnapshot(vec); + return; + case JSType::JS_API_DEQUE_ITERATOR: + JSAPIDequeIterator::Cast(obj)->DumpForSnapshot(vec); + return; + case JSType::JS_API_STACK: + JSAPIStack::Cast(obj)->DumpForSnapshot(vec); + return; + case JSType::JS_API_STACK_ITERATOR: + JSAPIStackIterator::Cast(obj)->DumpForSnapshot(vec); + return; case JSType::SOURCE_TEXT_MODULE_RECORD: SourceTextModule::Cast(obj)->DumpForSnapshot(vec); return; @@ -3211,6 +3276,31 @@ void JSAPIQueueIterator::DumpForSnapshot(std::vector> &vec) const +{ + JSObject::DumpForSnapshot(vec); +} + +void JSAPIDequeIterator::DumpForSnapshot(std::vector> &vec) const +{ + JSAPIDeque *deque = JSAPIDeque::Cast(GetIteratedDeque().GetTaggedObject()); + deque->DumpForSnapshot(vec); + vec.push_back(std::make_pair(CString("NextIndex"), JSTaggedValue(GetNextIndex()))); + JSObject::DumpForSnapshot(vec); +} +void JSAPIStack::DumpForSnapshot(std::vector> &vec) const +{ + JSObject::DumpForSnapshot(vec); +} + +void JSAPIStackIterator::DumpForSnapshot(std::vector> &vec) const +{ + JSAPIStack *stack = JSAPIStack::Cast(GetIteratedStack().GetTaggedObject()); + stack->DumpForSnapshot(vec); + vec.push_back(std::make_pair(CString("NextIndex"), JSTaggedValue(GetNextIndex()))); + JSObject::DumpForSnapshot(vec); +} + void JSArrayIterator::DumpForSnapshot(std::vector> &vec) const { JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); @@ -3387,6 +3477,8 @@ void GlobalEnv::DumpForSnapshot(std::vector> & vec.push_back(std::make_pair(CString("TreeMapIteratorPrototype"), globalConst->GetTreeMapIteratorPrototype())); vec.push_back(std::make_pair(CString("TreeSetIteratorPrototype"), globalConst->GetTreeSetIteratorPrototype())); vec.push_back(std::make_pair(CString("QueueIteratorPrototype"), globalConst->GetQueueIteratorPrototype())); + vec.push_back(std::make_pair(CString("DequeIteratorPrototype"), globalConst->GetDequeIteratorPrototype())); + vec.push_back(std::make_pair(CString("StackIteratorPrototype"), globalConst->GetStackIteratorPrototype())); } void JSDataView::DumpForSnapshot(std::vector> &vec) const diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index c78e9005..885b52c9 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -28,7 +28,9 @@ #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" #include "ecmascript/js_api_arraylist_iterator.h" +#include "ecmascript/js_api_deque_iterator.h" #include "ecmascript/js_api_queue_iterator.h" +#include "ecmascript/js_api_stack_iterator.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/jspandafile/class_info_extractor.h" @@ -174,8 +176,12 @@ void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHCl SetConstant( ConstantIndex::JS_API_ARRAYLIST_ITERATOR_CLASS_INDEX, factory->NewEcmaDynClass(dynClassClass, JSAPIArrayListIterator::SIZE, JSType::JS_API_ARRAYLIST_ITERATOR)); + SetConstant(ConstantIndex::JS_API_DEQUE_ITERATOR_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSAPIDequeIterator::SIZE, JSType::JS_API_DEQUE_ITERATOR)); SetConstant(ConstantIndex::JS_API_QUEUE_ITERATOR_CLASS_INDEX, factory->NewEcmaDynClass(dynClassClass, JSAPIQueueIterator::SIZE, JSType::JS_API_QUEUE_ITERATOR)); + SetConstant(ConstantIndex::JS_API_STACK_ITERATOR_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSAPIStackIterator::SIZE, JSType::JS_API_STACK_ITERATOR)); SetConstant(ConstantIndex::JS_API_TREE_MAP_ITERATOR_CLASS_INDEX, factory->NewEcmaDynClass(dynClassClass, JSAPITreeMapIterator::SIZE, JSType::JS_API_TREEMAP_ITERATOR)); SetConstant(ConstantIndex::JS_API_TREE_SET_ITERATOR_CLASS_INDEX, @@ -215,6 +221,8 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) SetConstant(ConstantIndex::TREEMAP_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); SetConstant(ConstantIndex::TREESET_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); SetConstant(ConstantIndex::QUEUE_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::DEQUE_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::STACK_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); /* SymbolTable *RegisterSymbols */ SetConstant(ConstantIndex::NAME_STRING_INDEX, factory->NewFromCanBeCompressString("name")); SetConstant(ConstantIndex::GETPROTOTYPEOF_STRING_INDEX, factory->NewFromCanBeCompressString("getPrototypeOf")); diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index de13447e..68a46df3 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -83,9 +83,12 @@ class JSThread; V(JSTaggedValue, JSMapIteratorClass, JS_MAP_ITERATOR_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, JSArrayIteratorClass, JS_ARRAY_ITERATOR_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, JSAPIArrayListIteratorClass, JS_API_ARRAYLIST_ITERATOR_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSAPIDequeIteratorClass, JS_API_DEQUE_ITERATOR_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, JSAPIQueueIteratorClass, JS_API_QUEUE_ITERATOR_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSAPIStackIteratorClass, JS_API_STACK_ITERATOR_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, JSAPITreeMapIteratorClass, JS_API_TREE_MAP_ITERATOR_CLASS_INDEX, ecma_roots_class) \ - V(JSTaggedValue, JSAPITreeSetIteratorClass, JS_API_TREE_SET_ITERATOR_CLASS_INDEX, ecma_roots_class) + V(JSTaggedValue, JSAPITreeSetIteratorClass, JS_API_TREE_SET_ITERATOR_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSAPIIteratorFuncDynClass, JS_API_ITERATOR_FUNC_DYN_CLASS_INDEX, ecma_roots_class) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GLOBAL_ENV_CONSTANT_SPECIAL(V) \ @@ -113,6 +116,8 @@ class JSThread; V(JSTaggedValue, TreeMapIteratorPrototype, TREEMAP_ITERATOR_PROTOTYPE_INDEX, TreeMapIterator) \ V(JSTaggedValue, TreeSetIteratorPrototype, TREESET_ITERATOR_PROTOTYPE_INDEX, TreeSetIterator) \ V(JSTaggedValue, QueueIteratorPrototype, QUEUE_ITERATOR_PROTOTYPE_INDEX, QueueIterator) \ + V(JSTaggedValue, DequeIteratorPrototype, DEQUE_ITERATOR_PROTOTYPE_INDEX, DequeIterator) \ + V(JSTaggedValue, StackIteratorPrototype, STACK_ITERATOR_PROTOTYPE_INDEX, StackIterator) \ /* SymbolTable*RegisterSymbols */ \ V(JSTaggedValue, NameString, NAME_STRING_INDEX, name) \ V(JSTaggedValue, GetPrototypeOfString, GETPROTOTYPEOF_STRING_INDEX, getPrototypeOf) \ diff --git a/ecmascript/interpreter/fast_runtime_stub-inl.h b/ecmascript/interpreter/fast_runtime_stub-inl.h index 52e862d9..5fe7c855 100644 --- a/ecmascript/interpreter/fast_runtime_stub-inl.h +++ b/ecmascript/interpreter/fast_runtime_stub-inl.h @@ -22,7 +22,9 @@ #include "ecmascript/global_env.h" #include "ecmascript/internal_call_params.h" #include "ecmascript/js_api_arraylist.h" +#include "ecmascript/js_api_deque.h" #include "ecmascript/js_api_queue.h" +#include "ecmascript/js_api_stack.h" #include "ecmascript/js_function.h" #include "ecmascript/js_hclass-inl.h" #include "ecmascript/js_proxy.h" @@ -1363,6 +1365,12 @@ JSTaggedValue FastRuntimeStub::GetContainerProperty(JSThread *thread, JSTaggedVa case JSType::JS_API_QUEUE: res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Get(thread, index); break; + case JSType::JS_API_DEQUE: + res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Get(index); + break; + case JSType::JS_API_STACK: + res = JSAPIStack::Cast(receiver.GetTaggedObject())->Get(index); + break; default: break; } @@ -1380,6 +1388,12 @@ JSTaggedValue FastRuntimeStub::SetContainerProperty(JSThread *thread, JSTaggedVa case JSType::JS_API_QUEUE: res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Set(thread, index, value); break; + case JSType::JS_API_DEQUE: + res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Set(thread, index, value); + break; + case JSType::JS_API_STACK: + res = JSAPIStack::Cast(receiver.GetTaggedObject())->Set(thread, index, value); + break; default: break; } diff --git a/ecmascript/js_api_deque.cpp b/ecmascript/js_api_deque.cpp new file mode 100644 index 00000000..c8ac13db --- /dev/null +++ b/ecmascript/js_api_deque.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2022 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_deque.h" + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +void JSAPIDeque::InsertFront(JSThread *thread, const JSHandle &deque, const JSHandle &value) +{ + JSHandle elements(thread, deque->GetElements()); + ASSERT(!elements->IsDictionaryMode()); + uint32_t capacity = elements->GetLength(); + uint32_t first = deque->GetFirst(); + uint32_t last = deque->GetLast(); + ASSERT(capacity != 0); + if ((first + capacity - 1) % capacity == last) { + elements = GrowCapacity(thread, deque, capacity, first, last); + ASSERT(!elements->IsDictionaryMode()); + deque->SetLast(capacity - 1); + first = 0; + } + capacity = elements->GetLength(); + ASSERT(capacity != 0); + first = (first + capacity - 1) % capacity; + elements->Set(thread, first, value); + deque->SetFirst(first); +} + +void JSAPIDeque::InsertEnd(JSThread *thread, const JSHandle &deque, const JSHandle &value) +{ + JSHandle elements(thread, deque->GetElements()); + ASSERT(!elements->IsDictionaryMode()); + uint32_t capacity = elements->GetLength(); + uint32_t first = deque->GetFirst(); + uint32_t last = deque->GetLast(); + ASSERT(capacity != 0); + if (first == (last + 1) % capacity) { + elements = GrowCapacity(thread, deque, capacity, first, last); + ASSERT(!elements->IsDictionaryMode()); + deque->SetFirst(0); + last = capacity - 1; + } + elements->Set(thread, last, value); + capacity = elements->GetLength(); + ASSERT(capacity != 0); + last = (last + 1) % capacity; + deque->SetLast(last); +} + +JSTaggedValue JSAPIDeque::GetFront() +{ + if (JSAPIDeque::IsEmpty()) { + return JSTaggedValue::Undefined(); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + ASSERT(!elements->IsDictionaryMode()); + return elements->Get(GetFirst()); +} + +JSTaggedValue JSAPIDeque::GetTail() +{ + if (JSAPIDeque::IsEmpty()) { + return JSTaggedValue::Undefined(); + } + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + ASSERT(!elements->IsDictionaryMode()); + uint32_t capacity = elements->GetLength(); + uint32_t last = GetLast(); + ASSERT(capacity != 0); + return elements->Get((last + capacity - 1) % capacity); +} + +JSHandle JSAPIDeque::GrowCapacity(JSThread *thread, const JSHandle &deque, + uint32_t oldCapacity, uint32_t first, uint32_t last) +{ + JSHandle oldElements(thread, deque->GetElements()); + ASSERT(!oldElements->IsDictionaryMode()); + uint32_t newCapacity = ComputeCapacity(oldCapacity); + uint32_t size = deque->GetSize(); + JSHandle newElements = + thread->GetEcmaVM()->GetFactory()->CopyDeque(oldElements, newCapacity, size, first, last); + deque->SetElements(thread, newElements); + return newElements; +} + +JSTaggedValue JSAPIDeque::PopFirst() +{ + if (JSAPIDeque::IsEmpty()) { + return JSTaggedValue::Undefined(); + } + uint32_t first = GetFirst(); + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + ASSERT(!elements->IsDictionaryMode()); + uint32_t capacity = elements->GetLength(); + JSTaggedValue firstElement = elements->Get(first); + ASSERT(capacity != 0); + first = (first + 1) % capacity; + SetFirst(first); + return firstElement; +} + +JSTaggedValue JSAPIDeque::PopLast() +{ + if (JSAPIDeque::IsEmpty()) { + return JSTaggedValue::Undefined(); + } + uint32_t last = GetLast(); + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + ASSERT(!elements->IsDictionaryMode()); + uint32_t capacity = elements->GetLength(); + ASSERT(capacity != 0); + last = (last + capacity - 1) % capacity; + JSTaggedValue lastElement = elements->Get(last); + SetLast(last); + return lastElement; +} + +bool JSAPIDeque::IsEmpty() +{ + uint32_t first = GetFirst(); + uint32_t last = GetLast(); + return first == last; +} + +uint32_t JSAPIDeque::GetSize() const +{ + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + uint32_t capacity = elements->GetLength(); + uint32_t first = GetFirst(); + uint32_t last = GetLast(); + ASSERT(capacity != 0); + return (last - first + capacity) % capacity; +} + +JSTaggedValue JSAPIDeque::Get(const uint32_t index) +{ + ASSERT(index < GetSize()); + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + uint32_t capacity = elements->GetLength(); + uint32_t first = GetFirst(); + ASSERT(capacity != 0); + uint32_t curIndex = (first + index) % capacity; + return elements->Get(curIndex); +} + +JSTaggedValue JSAPIDeque::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) +{ + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + uint32_t capacity = elements->GetLength(); + uint32_t first = GetFirst(); + ASSERT(capacity != 0); + uint32_t curIndex = (first + index) % capacity; + elements->Set(thread, curIndex, value); + return JSTaggedValue::Undefined(); +} + +bool JSAPIDeque::Has(JSTaggedValue value) const +{ + uint32_t first = GetFirst(); + uint32_t last = GetLast(); + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + uint32_t capacity = elements->GetLength(); + uint32_t index = first; + while (index != last) { + if (JSTaggedValue::SameValue(elements->Get(index), value)) { + return true; + } + ASSERT(capacity != 0); + index = (index + 1) % capacity; + } + return false; +} + +JSHandle JSAPIDeque::OwnKeys(JSThread *thread, const JSHandle &deque) +{ + uint32_t length = deque->GetSize(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys = factory->NewTaggedArray(length); + + JSHandle oldElements(thread, deque->GetElements()); + ASSERT(!oldElements->IsDictionaryMode()); + uint32_t oldCapacity = oldElements->GetLength(); + uint32_t newCapacity = ComputeCapacity(oldCapacity); + uint32_t firstIndex = deque->GetFirst(); + uint32_t lastIndex = deque->GetLast(); + uint32_t size = deque->GetSize(); + JSHandle newElements = + thread->GetEcmaVM()->GetFactory()->CopyDeque(oldElements, newCapacity, size, firstIndex, lastIndex); + deque->SetFirst(0); + deque->SetLast(size - 1); + deque->SetElements(thread, newElements); + + for (uint32_t i = 0; i < size; i++) { + keys->Set(thread, i, JSTaggedValue(i)); + } + + return keys; +} + +bool JSAPIDeque::GetOwnProperty(JSThread *thread, const JSHandle &deque, + const JSHandle &key, PropertyDescriptor &desc) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); + } + + uint32_t length = deque->GetSize(); + if (index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); + } + return JSObject::GetOwnProperty(thread, JSHandle::Cast(deque), key, desc); +} + +JSTaggedValue JSAPIDeque::GetIteratorObj(JSThread *thread, const JSHandle &deque) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iter(factory->NewJSAPIDequeIterator(deque)); + + return iter.GetTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_deque.h b/ecmascript/js_api_deque.h new file mode 100644 index 00000000..afce0c6a --- /dev/null +++ b/ecmascript/js_api_deque.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 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_JSAPIDEQUE_H +#define ECMASCRIPT_JSAPIDEQUE_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSAPIDeque : public JSObject { +public: + static constexpr int DEFAULT_CAPACITY_LENGTH = 8; + static JSAPIDeque *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSAPIDeque()); + return static_cast(object); + } + + static void InsertFront(JSThread *thread, const JSHandle &deque, const JSHandle &value); + + static void InsertEnd(JSThread *thread, const JSHandle &deque, const JSHandle &value); + + static JSTaggedValue ForEach(JSThread *thread, const JSHandle &deque); + + static JSTaggedValue GetIteratorObj(JSThread *thread, const JSHandle &deque); + + static JSHandle OwnKeys(JSThread *thread, const JSHandle &deque); + + static bool GetOwnProperty(JSThread *thread, const JSHandle &deque, const JSHandle &key, + PropertyDescriptor &desc); + + JSTaggedValue GetFront(); + + JSTaggedValue GetTail(); + + JSTaggedValue PopFirst(); + + JSTaggedValue PopLast(); + + JSTaggedValue Get(const uint32_t index); + + JSTaggedValue Set(JSThread *thread, const uint32_t index, JSTaggedValue value); + + uint32_t GetSize() const; + + bool Has(JSTaggedValue value) const; + + static constexpr size_t FIRST_OFFSET = JSObject::SIZE; + ACCESSORS_PRIMITIVE_FIELD(First, uint32_t, FIRST_OFFSET, LAST_OFFSET) + ACCESSORS_PRIMITIVE_FIELD(Last, uint32_t, LAST_OFFSET, ENDL_OFFSET) + DEFINE_ALIGN_SIZE(ENDL_OFFSET); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ENDL_OFFSET, ENDL_OFFSET) + DECL_DUMP() +private: + inline static uint32_t ComputeCapacity(uint32_t oldCapacity) + { + uint32_t newCapacity = oldCapacity << 1U; + return newCapacity > DEFAULT_CAPACITY_LENGTH ? newCapacity : DEFAULT_CAPACITY_LENGTH; + } + static JSHandle GrowCapacity(JSThread *thread, const JSHandle &deque, + uint32_t oldCapacity, uint32_t first, uint32_t last); + bool IsEmpty(); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSAPIDEQUE_H diff --git a/ecmascript/js_api_deque_iterator.cpp b/ecmascript/js_api_deque_iterator.cpp new file mode 100644 index 00000000..f4905b22 --- /dev/null +++ b/ecmascript/js_api_deque_iterator.cpp @@ -0,0 +1,59 @@ + +/* + * Copyright (c) 2022 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_deque_iterator.h" +#include "builtins/builtins_errors.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/base/typed_array_helper.h" +#include "global_env.h" +#include "js_api_deque.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +// DequeIteratorPrototype%.next ( ) +JSTaggedValue JSAPIDequeIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle input(BuiltinsBase::GetThis(argv)); + + if (!input->IsJSAPIDequeIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an deque iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + JSHandle deque(thread, iter->GetIteratedDeque()); + JSHandle undefinedHandle = thread->GlobalConstants()->GetHandledUndefined(); + if (deque->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + uint32_t index = iter->GetNextIndex(); + + JSHandle elements(thread, JSHandle(deque)->GetElements()); + array_size_t capacity = elements->GetLength(); + uint32_t last = JSHandle(deque)->GetLast(); + if (index == last) { + iter->SetIteratedDeque(thread, undefinedHandle); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + ASSERT(capacity != 0); + iter->SetNextIndex((index + 1) % capacity); + JSHandle value = JSTaggedValue::GetProperty(thread, deque, index).GetValue(); + + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_deque_iterator.h b/ecmascript/js_api_deque_iterator.h new file mode 100644 index 00000000..eee36391 --- /dev/null +++ b/ecmascript/js_api_deque_iterator.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 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_DEQUE_ITERATOR_H +#define ECMASCRIPT_JS_API_DEQUE_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSAPIDequeIterator : public JSObject { +public: + static JSAPIDequeIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSAPIDequeIterator()); + return static_cast(obj); + } + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ITERATED_DEQUE_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedDeque, ITERATED_DEQUE_OFFSET, NEXT_INDEX_OFFSET) + ACCESSORS_PRIMITIVE_FIELD(NextIndex, uint32_t, NEXT_INDEX_OFFSET, LAST_OFFSET) + DEFINE_ALIGN_SIZE(LAST_OFFSET); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_DEQUE_OFFSET, NEXT_INDEX_OFFSET) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_API_DEQUE_ITERATOR_H diff --git a/ecmascript/js_api_stack.cpp b/ecmascript/js_api_stack.cpp new file mode 100644 index 00000000..2d75b795 --- /dev/null +++ b/ecmascript/js_api_stack.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022 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_stack.h" + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +bool JSAPIStack::Empty() +{ + if (this->GetTop() == -1) { + return true; + } + return false; +} + +JSTaggedValue JSAPIStack::Push(JSThread *thread, const JSHandle &stack, + const JSHandle &value) +{ + int top = static_cast(stack->GetTop()); + JSHandle elements = GrowCapacity(thread, stack, top + 1); + + ASSERT(!elements->IsDictionaryMode()); + elements->Set(thread, top + 1, value); + stack->SetTop(++top); + return value.GetTaggedValue(); +} + +JSTaggedValue JSAPIStack::Peek() +{ + int top = this->GetTop(); + if (top == -1) { + return JSTaggedValue::Undefined(); + } + + TaggedArray *elements = TaggedArray::Cast(this->GetElements().GetTaggedObject()); + ASSERT(!elements->IsDictionaryMode()); + return elements->Get(top); +} + +JSTaggedValue JSAPIStack::Pop() +{ + int top = this->GetTop(); + if (top == -1) { + return JSTaggedValue::Undefined(); + } + TaggedArray *elements = TaggedArray::Cast(this->GetElements().GetTaggedObject()); + ASSERT(!elements->IsDictionaryMode()); + this->SetTop(--top); + return elements->Get(top + 1); +} + +int JSAPIStack::Search(const JSHandle &value) +{ + int top = this->GetTop(); + TaggedArray *elements = TaggedArray::Cast(this->GetElements().GetTaggedObject()); + ASSERT(!elements->IsDictionaryMode()); + for (int i = 0; i <= top; i++) { + if (value.GetTaggedValue() == elements->Get(i)) { + return i; + } + } + return -1; +} + +JSHandle JSAPIStack::GrowCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity) +{ + JSHandle oldElements(thread, obj->GetElements()); + uint32_t oldLength = oldElements->GetLength(); + if (capacity < oldLength) { + return oldElements; + } + uint32_t newCapacity = ComputeCapacity(capacity); + JSHandle newElements = + thread->GetEcmaVM()->GetFactory()->CopyArray(oldElements, oldLength, newCapacity); + + obj->SetElements(thread, newElements); + return newElements; +} + + +JSTaggedValue JSAPIStack::Get(const uint32_t index) +{ + ASSERT(static_cast(index) <= GetTop()); + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + return elements->Get(index); +} + +JSTaggedValue JSAPIStack::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) +{ + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + elements->Set(thread, index, value); + return JSTaggedValue::Undefined(); +} + +bool JSAPIStack::Has(JSTaggedValue value) const +{ + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + int top = static_cast(GetTop()); + if (top == -1) { + return false; + } + + for (int i = 0; i < top + 1; i++) { + if (JSTaggedValue::SameValue(elements->Get(i), value)) { + return true; + } + } + return false; +} + +JSHandle JSAPIStack::OwnKeys(JSThread *thread, const JSHandle &obj) +{ + uint32_t top = obj->GetTop(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys = factory->NewTaggedArray(top); + + for (uint32_t i = 0; i < top + 1; i++) { + keys->Set(thread, i, JSTaggedValue(i)); + } + + return keys; +} + +bool JSAPIStack::GetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); + } + + uint32_t length = static_cast(obj->GetTop()) + 1; + if (index + 1 > length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); + } + return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_stack.h b/ecmascript/js_api_stack.h new file mode 100644 index 00000000..3a485b6c --- /dev/null +++ b/ecmascript/js_api_stack.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 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_JSAPISTACK_H +#define ECMASCRIPT_JSAPISTACK_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSAPIStack : public JSObject { +public: + static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static JSAPIStack *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSAPIStack()); + return static_cast(object); + } + + static JSTaggedValue Push(JSThread *thread, const JSHandle &stack, + const JSHandle &value); + + static JSHandle OwnKeys(JSThread *thread, const JSHandle &obj); + + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc); + + bool Empty(); + + bool Has(JSTaggedValue value) const; + + int Search(const JSHandle &value); + + inline int GetSize() const + { + return GetTop(); + } + + JSTaggedValue Peek(); + + JSTaggedValue Pop(); + + JSTaggedValue Get(const uint32_t index); + + JSTaggedValue Set(JSThread *thread, const uint32_t index, JSTaggedValue value); + + static constexpr size_t TOP_OFFSET = JSObject::SIZE; + ACCESSORS_PRIMITIVE_FIELD(Top, int, TOP_OFFSET, LAST_OFFSET) + DEFINE_ALIGN_SIZE(LAST_OFFSET); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LAST_OFFSET, LAST_OFFSET) + DECL_DUMP() +private: + inline static uint32_t ComputeCapacity(uint32_t oldCapacity) + { + uint32_t newCapacity = oldCapacity + (oldCapacity >> 1U); + return newCapacity > DEFAULT_CAPACITY_LENGTH ? newCapacity : DEFAULT_CAPACITY_LENGTH; + } + static JSHandle GrowCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSAPISTACK_H diff --git a/ecmascript/js_api_stack_iterator.cpp b/ecmascript/js_api_stack_iterator.cpp new file mode 100644 index 00000000..6d8bade8 --- /dev/null +++ b/ecmascript/js_api_stack_iterator.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 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_stack_iterator.h" +#include "builtins/builtins_errors.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/base/typed_array_helper.h" +#include "global_env.h" +#include "js_api_stack.h" +#include "object_factory.h" +#include "js_hclass.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +// StackIteratorPrototype%.next() +JSTaggedValue JSAPIStackIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle input(BuiltinsBase::GetThis(argv)); + + if (!input->IsJSAPIStackIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an stack iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + JSHandle stack(thread, iter->GetIteratedStack()); + JSHandle undefinedHandle = thread->GlobalConstants()->GetHandledUndefined(); + if (stack->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + uint32_t index = iter->GetNextIndex(); + + uint32_t length = static_cast((JSHandle::Cast(stack))->GetSize()) + 1; + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (index + 1 > length) { + iter->SetIteratedStack(thread, undefinedHandle); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + iter->SetNextIndex(index + 1); + JSHandle key(thread, JSTaggedValue(index)); + JSHandle value = JSTaggedValue::GetProperty(thread, stack, key).GetValue(); + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_stack_iterator.h b/ecmascript/js_api_stack_iterator.h new file mode 100644 index 00000000..2aa68e87 --- /dev/null +++ b/ecmascript/js_api_stack_iterator.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 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_STACK_ITERATOR_H +#define ECMASCRIPT_JS_API_STACK_ITERATOR_H + +#include "js_api_stack.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSAPIStackIterator : public JSObject { +public: + static JSAPIStackIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSAPIStackIterator()); + return static_cast(obj); + } + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ITERATED_STACK_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedStack, ITERATED_STACK_OFFSET, NEXT_INDEX_OFFSET) + ACCESSORS_PRIMITIVE_FIELD(NextIndex, uint32_t, NEXT_INDEX_OFFSET, LAST_OFFSET) + DEFINE_ALIGN_SIZE(LAST_OFFSET); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_STACK_OFFSET, NEXT_INDEX_OFFSET) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_API_STACK_ITERATOR_H diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 6379a066..8911f530 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -97,7 +97,9 @@ class ProtoChangeDetails; JS_MAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_SET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_ARRAYLIST_ITERATOR, /* /////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_DEQUE_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_QUEUE_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_STACK_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREEMAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREESET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_ARRAY_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -123,6 +125,8 @@ class ProtoChangeDetails; JS_API_VECTOR, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREE_MAP, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREE_SET, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_DEQUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_STACK, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_QUEUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_TYPED_ARRAY, /* JS_TYPED_ARRAY_BEGIN /////////////////////////////////////////////////////////////////// */ \ JS_INT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -646,6 +650,17 @@ public: { return GetObjectType() == JSType::JS_API_ARRAYLIST_ITERATOR; } + + inline bool IsJSAPIStack() const + { + return GetObjectType() == JSType::JS_API_STACK; + } + + inline bool IsJSAPIDeque() const + { + return GetObjectType() == JSType::JS_API_DEQUE; + } + inline bool IsJSAPIQueue() const { return GetObjectType() == JSType::JS_API_QUEUE; @@ -723,6 +738,16 @@ public: return GetObjectType() == JSType::JS_ARRAY_ITERATOR; } + inline bool IsJSAPIDequeIterator() const + { + return GetObjectType() == JSType::JS_API_DEQUE_ITERATOR; + } + + inline bool IsJSAPIStackIterator() const + { + return GetObjectType() == JSType::JS_API_STACK_ITERATOR; + } + inline bool IsPrototypeHandler() const { return GetObjectType() == JSType::PROTOTYPE_HANDLER; diff --git a/ecmascript/js_object-inl.h b/ecmascript/js_object-inl.h index 32b9c924..0fad735e 100644 --- a/ecmascript/js_object-inl.h +++ b/ecmascript/js_object-inl.h @@ -181,6 +181,11 @@ inline bool JSObject::IsJSAPIArrayListIterator() const return GetJSHClass()->IsJSAPIArrayListIterator(); } +inline bool JSObject::IsJSAPIStackIterator() const +{ + return GetJSHClass()->IsJSAPIStackIterator(); +} + inline bool JSObject::IsJSPrimitiveRef() const { return GetJSHClass()->IsJsPrimitiveRef(); diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h index f6d01340..d53ea124 100644 --- a/ecmascript/js_object.h +++ b/ecmascript/js_object.h @@ -539,6 +539,7 @@ public: bool IsJSMapIterator() const; bool IsJSArrayIterator() const; bool IsJSAPIArrayListIterator() const; + bool IsJSAPIStackIterator() const; bool IsJSPrimitiveRef() const; bool IsElementDict() const; bool IsPropertiesDict() const; diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index 28223787..81a24ca5 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -609,6 +609,15 @@ inline bool JSTaggedValue::IsJSAPIQueue() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIQueue(); } +inline bool JSTaggedValue::IsJSAPIDeque() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIDeque(); +} +inline bool JSTaggedValue::IsJSAPIStack() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIStack(); +} + inline bool JSTaggedValue::IsSpecialContainer() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsSpecialContainer(); @@ -902,6 +911,15 @@ inline bool JSTaggedValue::IsJSAPIQueueIterator() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIQueueIterator(); } +inline bool JSTaggedValue::IsJSAPIDequeIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIDequeIterator(); +} +inline bool JSTaggedValue::IsJSAPIStackIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIStackIterator(); +} + inline bool JSTaggedValue::IsIterator() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsIterator(); diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp index d40148e2..31aedb72 100644 --- a/ecmascript/js_tagged_value.cpp +++ b/ecmascript/js_tagged_value.cpp @@ -18,7 +18,9 @@ #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/internal_call_params.h" +#include "ecmascript/js_api_deque.h" #include "ecmascript/js_api_queue.h" +#include "ecmascript/js_api_stack.h" #include "ecmascript/js_array.h" #include "ecmascript/js_api_arraylist.h" #include "ecmascript/js_handle.h" @@ -898,6 +900,12 @@ bool JSTaggedValue::HasContainerProperty(JSThread *thread, const JSHandle::Cast(obj)->Has(key.GetTaggedValue()); } + case JSType::JS_API_DEQUE: { + return JSHandle::Cast(obj)->Has(key.GetTaggedValue()); + } + case JSType::JS_API_STACK: { + return JSHandle::Cast(obj)->Has(key.GetTaggedValue()); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::HasProperty(thread, JSHandle(obj), key); @@ -920,6 +928,12 @@ JSHandle JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *threa case JSType::JS_API_QUEUE: { return JSAPIQueue::OwnKeys(thread, JSHandle::Cast(obj)); } + case JSType::JS_API_DEQUE: { + return JSAPIDeque::OwnKeys(thread, JSHandle::Cast(obj)); + } + case JSType::JS_API_STACK: { + return JSAPIStack::OwnKeys(thread, JSHandle::Cast(obj)); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); @@ -943,6 +957,12 @@ bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle::Cast(obj), key, desc); } + case JSType::JS_API_DEQUE: { + return JSAPIDeque::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + } + case JSType::JS_API_STACK: { + return JSAPIStack::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index 5eadce46..c76c35bf 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -333,6 +333,10 @@ public: bool IsJSAPITreeSetIterator() const; bool IsJSAPIQueue() const; bool IsJSAPIQueueIterator() const; + bool IsJSAPIDeque() const; + bool IsJSAPIDequeIterator() const; + bool IsJSAPIStack() const; + bool IsJSAPIStackIterator() const; bool IsSpecialContainer() const; bool IsPrototypeHandler() const; diff --git a/ecmascript/mem/object_xray-inl.h b/ecmascript/mem/object_xray-inl.h index 27db76f2..748cbc00 100644 --- a/ecmascript/mem/object_xray-inl.h +++ b/ecmascript/mem/object_xray-inl.h @@ -23,8 +23,12 @@ #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_deque_iterator.h" #include "ecmascript/js_api_queue.h" #include "ecmascript/js_api_queue_iterator.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" #include "ecmascript/jspandafile/class_info_extractor.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/js_api_tree_map.h" @@ -351,6 +355,18 @@ void ObjectXRay::VisitObjectBody(TaggedObject *object, JSHClass *klass, const Ec case JSType::JS_API_TREESET_ITERATOR: JSAPITreeSetIterator::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::JS_API_DEQUE: + JSAPIDeque::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_DEQUE_ITERATOR: + JSAPIDequeIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_STACK: + JSAPIStack::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_STACK_ITERATOR: + JSAPIStackIterator::Cast(object)->VisitRangeSlot(visitor); + break; case JSType::BIGINT: BigInt::Cast(object)->VisitRangeSlot(visitor); break; diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index ee5ea7f5..9deb50bf 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -33,8 +33,12 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_deque_iterator.h" #include "ecmascript/js_api_queue.h" #include "ecmascript/js_api_queue_iterator.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" #include "ecmascript/jspandafile/class_info_extractor.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/js_api_tree_map.h" @@ -797,6 +801,13 @@ JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandleSetFront(0); JSAPIQueue::Cast(*obj)->SetTail(0); break; + case JSType::JS_API_STACK: + JSAPIStack::Cast(*obj)->SetTop(0); + break; + case JSType::JS_API_DEQUE: + JSAPIDeque::Cast(*obj)->SetFirst(0); + JSAPIDeque::Cast(*obj)->SetLast(0); + break; case JSType::JS_FUNCTION: case JSType::JS_GENERATOR_FUNCTION: case JSType::JS_FORIN_ITERATOR: @@ -806,6 +817,8 @@ JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandle ObjectFactory::NewJSAPIArrayListIterator(const return iter; } +JSHandle ObjectFactory::NewJSAPIStackIterator(const JSHandle &stack) +{ + NewObjectHook(); + JSHandle protoValue(thread_, thread_->GlobalConstants()->GetStackIteratorPrototype()); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle dynHandle(globalConst->GetHandledJSAPIStackIteratorClass()); + dynHandle->SetPrototype(thread_, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedStack(thread_, stack); + iter->SetNextIndex(0); + return iter; +} + +JSHandle ObjectFactory::CopyDeque(const JSHandle &old, uint32_t newLength, + [[maybe_unused]] uint32_t oldLength, uint32_t first, uint32_t last) +{ + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heap_->AllocateYoungOrHugeObject( + JSHClass::Cast(thread_->GlobalConstants()->GetArrayClass().GetTaggedObject()), size); + JSHandle newArray(thread_, header); + + uint32_t curIndex = first; + // newIndex use in new TaggedArray, 0 : New TaggedArray index + uint32_t newIndex = 0; + uint32_t oldCapacity = old->GetLength(); + newArray->SetLength(newLength); + while (curIndex != last) { + JSTaggedValue value = old->Get(curIndex); + newArray->Set(thread_, newIndex, value); + ASSERT(oldCapacity != 0); + curIndex = (curIndex + 1) % oldCapacity; + newIndex = newIndex + 1; + } + return newArray; +} + +JSHandle ObjectFactory::NewJSAPIDequeIterator(const JSHandle &deque) +{ + NewObjectHook(); + JSHandle protoValue(thread_, thread_->GlobalConstants()->GetDequeIteratorPrototype()); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle dynHandle(globalConst->GetHandledJSAPIDequeIteratorClass()); + dynHandle->SetPrototype(thread_, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedDeque(thread_, deque); + iter->SetNextIndex(deque->GetFirst()); + return iter; +} + JSHandle ObjectFactory::CopyQueue(const JSHandle &old, uint32_t oldLength, uint32_t newLength, [[maybe_unused]] uint32_t front, [[maybe_unused]] uint32_t tail) diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index a75fd2e0..70eb700e 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -98,8 +98,12 @@ class TSFunctionType; class TSArrayType; class JSAPIArrayList; class JSAPIArrayListIterator; +class JSAPIDeque; +class JSAPIDequeIterator; class JSAPIQueue; class JSAPIQueueIterator; +class JSAPIStack; +class JSAPIStackIterator; class JSAPITreeSet; class JSAPITreeMap; class JSAPITreeSetIterator; @@ -397,9 +401,13 @@ public: JSHandle CopyQueue(const JSHandle &old, uint32_t oldLength, uint32_t newLength, uint32_t front, uint32_t tail); JSHandle NewJSAPIQueueIterator(const JSHandle &queue); + JSHandle CopyDeque(const JSHandle &old, uint32_t newLength, uint32_t oldLength, + uint32_t first, uint32_t last); + JSHandle NewJSAPIDequeIterator(const JSHandle &deque); JSHandle NewJSAPIArrayListIterator(const JSHandle &arrayList); JSHandle NewJSAPITreeMapIterator(const JSHandle &map, IterationKind kind); JSHandle NewJSAPITreeSetIterator(const JSHandle &set, IterationKind kind); + JSHandle NewJSAPIStackIterator(const JSHandle &stack); // --------------------------------------module-------------------------------------------- JSHandle NewModuleNamespace(); JSHandle NewImportEntry(); diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h index 1b426823..31d60bf5 100644 --- a/ecmascript/runtime_call_id.h +++ b/ecmascript/runtime_call_id.h @@ -588,6 +588,26 @@ namespace panda::ecmascript { V(TreeSet, Values) \ V(TreeSet, ForEach) \ V(TreeSet, Entries) \ + V(Deque, Constructor) \ + V(Deque, InsertFront) \ + V(Deque, InsertEnd) \ + V(Deque, GetFront) \ + V(Deque, GetTail) \ + V(Deque, Has) \ + V(Deque, PopFirst) \ + V(Deque, PopLast) \ + V(Deque, ForEach) \ + V(Deque, GetIteratorObj) \ + V(Deque, GetSize) \ + V(Stack, Constructor) \ + V(Stack, Iterator) \ + V(Stack, IsEmpty) \ + V(Stack, Push) \ + V(Stack, Peek) \ + V(Stack, Pop) \ + V(Stack, Locate) \ + V(Stack, ForEach) \ + V(Stack, GetLength) \ V(Queue, Constructor) \ V(Queue, Add) \ V(Queue, GetFirst) \ diff --git a/ecmascript/snapshot/mem/snapshot_serialize.cpp b/ecmascript/snapshot/mem/snapshot_serialize.cpp index 4ab79562..6bb6e94e 100644 --- a/ecmascript/snapshot/mem/snapshot_serialize.cpp +++ b/ecmascript/snapshot/mem/snapshot_serialize.cpp @@ -54,12 +54,16 @@ #include "ecmascript/builtins/builtins_weak_map.h" #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/containers/containers_arraylist.h" +#include "ecmascript/containers/containers_deque.h" #include "ecmascript/containers/containers_queue.h" +#include "ecmascript/containers/containers_stack.h" #include "ecmascript/containers/containers_treemap.h" #include "ecmascript/containers/containers_treeset.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/global_env.h" +#include "ecmascript/js_api_deque_iterator.h" #include "ecmascript/js_api_queue_iterator.h" +#include "ecmascript/js_api_stack_iterator.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_array_iterator.h" @@ -125,6 +129,8 @@ using ArrayList = containers::ContainersArrayList; using TreeMap = containers::ContainersTreeMap; using TreeSet = containers::ContainersTreeSet; using Queue = containers::ContainersQueue; +using Deque = containers::ContainersDeque; +using ContainerStack = panda::ecmascript::containers::ContainersStack; constexpr int TAGGED_SIZE = JSTaggedValue::TaggedTypeSize(); constexpr int OBJECT_HEADER_SIZE = TaggedObject::TaggedObjectSize(); @@ -628,6 +634,18 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(TreeSet::GetLength), reinterpret_cast(JSAPITreeMapIterator::Next), reinterpret_cast(JSAPITreeSetIterator::Next), + reinterpret_cast(Deque::DequeConstructor), + reinterpret_cast(Deque::InsertFront), + reinterpret_cast(Deque::InsertEnd), + reinterpret_cast(Deque::GetFirst), + reinterpret_cast(Deque::GetLast), + reinterpret_cast(Deque::Has), + reinterpret_cast(Deque::PopFirst), + reinterpret_cast(Deque::PopLast), + reinterpret_cast(Deque::ForEach), + reinterpret_cast(Deque::GetIteratorObj), + reinterpret_cast(Deque::GetSize), + reinterpret_cast(JSAPIDequeIterator::Next), reinterpret_cast(Queue::QueueConstructor), reinterpret_cast(Queue::Add), reinterpret_cast(Queue::GetFirst), @@ -636,6 +654,16 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(Queue::GetIteratorObj), reinterpret_cast(Queue::GetSize), reinterpret_cast(JSAPIQueueIterator::Next), + reinterpret_cast(ContainerStack::StackConstructor), + reinterpret_cast(ContainerStack::Iterator), + reinterpret_cast(ContainerStack::IsEmpty), + reinterpret_cast(ContainerStack::Push), + reinterpret_cast(ContainerStack::Peek), + reinterpret_cast(ContainerStack::Pop), + reinterpret_cast(ContainerStack::Locate), + reinterpret_cast(ContainerStack::ForEach), + reinterpret_cast(ContainerStack::GetLength), + reinterpret_cast(JSAPIStackIterator::Next), // not builtins method reinterpret_cast(JSFunction::PrototypeSetter), diff --git a/ecmascript/tests/BUILD.gn b/ecmascript/tests/BUILD.gn index de4d0d42..21a61439 100644 --- a/ecmascript/tests/BUILD.gn +++ b/ecmascript/tests/BUILD.gn @@ -352,6 +352,60 @@ host_unittest_action("JsMapTest") { } } +host_unittest_action("JSAPIDequeTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_api_deque_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("JSAPIStackTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_api_stack_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("JSAPITreeMapTest") { module_out_path = module_output_path @@ -935,6 +989,8 @@ group("unittest") { ":GcTest", ":GlueRegsTest", ":HugeObjectTest", + ":JSAPIDequeTest", + ":JSAPIStackTest", ":JSAPITreeMapTest", ":JSAPITreeSetTest", ":JsArgumentsTest", @@ -985,6 +1041,8 @@ group("host_unittest") { ":GcTestAction", ":GlueRegsTestAction", ":HugeObjectTestAction", + ":JSAPIDequeTestAction", + ":JSAPIStackTestAction", ":JSAPITreeMapTestAction", ":JSAPITreeSetTestAction", ":JsArgumentsTestAction", diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index 6365f4aa..27450551 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -27,8 +27,12 @@ #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_deque_iterator.h" #include "ecmascript/js_api_queue.h" #include "ecmascript/js_api_queue_iterator.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" #include "ecmascript/jspandafile/class_info_extractor.h" #include "ecmascript/jspandafile/program_object-inl.h" #include "ecmascript/js_api_tree_map.h" @@ -195,6 +199,14 @@ static JSHandle NewJSAPIArrayList(JSThread *thread, ObjectFactor return jsArrayList; } +static JSHandle NewJSAPIStack(ObjectFactory *factory, JSHandle proto) +{ + JSHandle stackClass = factory->NewEcmaDynClass(JSAPIStack::SIZE, JSType::JS_API_STACK, proto); + JSHandle jsStack = JSHandle::Cast(factory->NewJSObject(stackClass)); + jsStack->SetTop(0); + return jsStack; +} + static JSHandle NewJSAPIQueue(JSThread *thread, ObjectFactory *factory, JSHandle proto) { JSHandle queueClass = factory->NewEcmaDynClass(JSAPIQueue::SIZE, JSType::JS_API_QUEUE, proto); @@ -207,6 +219,17 @@ static JSHandle NewJSAPIQueue(JSThread *thread, ObjectFactory *facto return jsQueue; } +static JSHandle NewJSAPIDeque(JSThread *thread, ObjectFactory *factory, JSHandle proto) +{ + JSHandle dequeClass = factory->NewEcmaDynClass(JSAPIDeque::SIZE, JSType::JS_API_DEQUE, proto); + JSHandle jsDeque = JSHandle::Cast(factory->NewJSObject(dequeClass)); + JSHandle newElements = factory->NewTaggedArray(JSAPIDeque::DEFAULT_CAPACITY_LENGTH); + jsDeque->SetFirst(0); + jsDeque->SetLast(0); + jsDeque->SetElements(thread, newElements); + return jsDeque; +} + HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) { [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); @@ -779,6 +802,32 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) DUMP_FOR_HANDLE(jsTreeSetIter) break; } + case JSType::JS_API_DEQUE: { + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIDeque::SIZE, 1) + JSHandle jsDeque = NewJSAPIDeque(thread, factory, proto); + DUMP_FOR_HANDLE(jsDeque) + break; + } + case JSType::JS_API_DEQUE_ITERATOR: { + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIDequeIterator::SIZE, 2) + JSHandle jsDequeIter = + factory->NewJSAPIDequeIterator(NewJSAPIDeque(thread, factory, proto)); + DUMP_FOR_HANDLE(jsDequeIter) + break; + } + case JSType::JS_API_STACK: { + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIStack::SIZE, 1) + JSHandle jsStack = NewJSAPIStack(factory, proto); + DUMP_FOR_HANDLE(jsStack) + break; + } + case JSType::JS_API_STACK_ITERATOR: { + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIStackIterator::SIZE, 2) + JSHandle jsStackIter = + factory->NewJSAPIStackIterator(NewJSAPIStack(factory, proto)); + DUMP_FOR_HANDLE(jsStackIter) + break; + } case JSType::MODULE_RECORD: { CHECK_DUMP_FIELDS(Record::SIZE, ModuleRecord::SIZE, 0); break; diff --git a/ecmascript/tests/js_api_deque_test.cpp b/ecmascript/tests/js_api_deque_test.cpp new file mode 100644 index 00000000..ec469738 --- /dev/null +++ b/ecmascript/tests/js_api_deque_test.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2022 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 "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +using namespace panda::ecmascript::containers; + +namespace panda::test { +class JSAPIDequeTest : 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}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + +protected: + JSAPIDeque *CreateDeque() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromCanBeCompressString("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(containers::ContainerTag::Deque))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = containers::ContainersPrivate::Load(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle constructor(thread, result); + JSHandle deque(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle newElements = factory->NewTaggedArray(JSAPIDeque::DEFAULT_CAPACITY_LENGTH); + deque->SetElements(thread, newElements); + return *deque; + } +}; + +HWTEST_F_L0(JSAPIDequeTest, dequeCreate) +{ + JSAPIDeque *deque = CreateDeque(); + EXPECT_TRUE(deque != nullptr); +} + +HWTEST_F_L0(JSAPIDequeTest, InsertFrontAndGetFront) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateDeque()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIDeque::InsertFront(thread, toor, value); + EXPECT_EQ(toor->GetFront(), value.GetTaggedValue()); + } + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIDequeTest, InsertEndAndGetTail) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateDeque()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIDeque::InsertEnd(thread, toor, value); + EXPECT_EQ(toor->GetTail(), value.GetTaggedValue()); + } + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIDequeTest, PopFirst) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateDeque()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIDeque::InsertFront(thread, toor, value); + EXPECT_EQ(toor->PopFirst(), value.GetTaggedValue()); + } + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIDequeTest, PopLast) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateDeque()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIDeque::InsertEnd(thread, toor, value); + EXPECT_EQ(toor->PopLast(), value.GetTaggedValue()); + } + + toor->Dump(); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_api_stack_test.cpp b/ecmascript/tests/js_api_stack_test.cpp new file mode 100644 index 00000000..3981482f --- /dev/null +++ b/ecmascript/tests/js_api_stack_test.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2022 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 "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_api_stack.h" +#include "ecmascript/js_api_stack_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +using namespace panda::ecmascript::containers; + +namespace panda::test { +class JSAPIStackTest : 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}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + +protected: + JSAPIStack *CreateStack() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromCanBeCompressString("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(containers::ContainerTag::Stack))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = containers::ContainersPrivate::Load(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle constructor(thread, result); + JSHandle stack(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + stack->SetTop(-1); + return *stack; + } +}; + +HWTEST_F_L0(JSAPIStackTest, stackCreate) +{ + JSAPIStack *stack = CreateStack(); + EXPECT_TRUE(stack != nullptr); +} + +HWTEST_F_L0(JSAPIStackTest, PushAndPeek) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateStack()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSTaggedValue result = JSAPIStack::Push(thread, toor, value); + EXPECT_EQ(result, value.GetTaggedValue()); + EXPECT_EQ(toor->Peek(), value.GetTaggedValue()); + } + EXPECT_EQ(static_cast(toor->GetTop() + 1), NODE_NUMBERS); + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIStackTest, Pop) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateStack()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSTaggedValue result = JSAPIStack::Push(thread, toor, value); + EXPECT_EQ(result, value.GetTaggedValue()); + EXPECT_EQ(toor->Peek(), value.GetTaggedValue()); + } + + for (uint32_t i = NODE_NUMBERS; i < 0; i--) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSTaggedValue gValue = toor->Pop(); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIStackTest, Empty) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateStack()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSTaggedValue result = JSAPIStack::Push(thread, toor, value); + EXPECT_EQ(result, value.GetTaggedValue()); + EXPECT_EQ(toor->Peek(), value.GetTaggedValue()); + EXPECT_EQ(toor->Empty(), false); + } + + int num = 8 ; + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + + toor->Pop(); + if (num == -1) { + EXPECT_EQ(toor->Empty(), true); + } + } + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIStackTest, Search) +{ + constexpr uint32_t NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateStack()); + + std::string myValue("myvalue"); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSTaggedValue result = JSAPIStack::Push(thread, toor, value); + EXPECT_EQ(result, value.GetTaggedValue()); + EXPECT_EQ(toor->Search(value), static_cast(i)); + } + + toor->Dump(); +} +} // namespace panda::test