From 666eb6b8bdb87f501e7a63eb98b08c263ba2bb43 Mon Sep 17 00:00:00 2001 From: chenqi Date: Thu, 2 Jun 2022 09:11:52 +0800 Subject: [PATCH] Add Containers List And LinkedList Description 1.To ensure the high performance of container classes, List and LinkedList is provided in ark. 2.modify Copyright 2021->2022. 3.fix containers. Related issue #I58XA9:Add Containers List And LinkedList. Signed-off-by: chenqi --- BUILD.gn | 7 + .../containers/containers_arraylist.cpp | 11 +- .../containers/containers_linked_list.cpp | 428 +++++++++++ .../containers/containers_linked_list.h | 51 ++ ecmascript/containers/containers_list.cpp | 409 +++++++++++ ecmascript/containers/containers_list.h | 49 ++ .../containers/containers_plainarray.cpp | 14 +- ecmascript/containers/containers_private.cpp | 167 ++++- ecmascript/containers/containers_private.h | 4 + ecmascript/containers/tests/BUILD.gn | 2 + .../tests/containers_linked_list_test.cpp | 453 ++++++++++++ .../containers/tests/containers_list_test.cpp | 461 ++++++++++++ ecmascript/dfx/hprof/heap_snapshot.cpp | 4 + ecmascript/dump.cpp | 194 ++++- ecmascript/global_env_constants.cpp | 11 + ecmascript/global_env_constants.h | 6 + ecmascript/ic/ic_runtime.cpp | 2 +- .../interpreter/fast_runtime_stub-inl.h | 26 +- ecmascript/js_api_arraylist.cpp | 36 +- ecmascript/js_api_arraylist.h | 10 +- ecmascript/js_api_arraylist_iterator.cpp | 2 +- ecmascript/js_api_arraylist_iterator.h | 2 +- ecmascript/js_api_deque.cpp | 22 +- ecmascript/js_api_deque.h | 6 +- ecmascript/js_api_deque_iterator.cpp | 14 +- ecmascript/js_api_linked_list.cpp | 234 ++++++ ecmascript/js_api_linked_list.h | 72 ++ ecmascript/js_api_linked_list_iterator.cpp | 66 ++ ecmascript/js_api_linked_list_iterator.h | 43 ++ ecmascript/js_api_list.cpp | 218 ++++++ ecmascript/js_api_list.h | 75 ++ ecmascript/js_api_list_iterator.cpp | 63 ++ ecmascript/js_api_list_iterator.h | 43 ++ ecmascript/js_api_plain_array.cpp | 14 +- ecmascript/js_api_plain_array.h | 2 +- ecmascript/js_api_queue.cpp | 38 +- ecmascript/js_api_queue.h | 7 +- ecmascript/js_api_queue_iterator.cpp | 2 +- ecmascript/js_api_queue_iterator.h | 2 +- ecmascript/js_api_stack.cpp | 23 +- ecmascript/js_api_stack.h | 6 +- ecmascript/js_api_tree_map.cpp | 2 +- ecmascript/js_api_tree_map.h | 2 +- ecmascript/js_api_tree_map_iterator.cpp | 2 +- ecmascript/js_api_tree_map_iterator.h | 2 +- ecmascript/js_api_tree_set.cpp | 2 +- ecmascript/js_api_tree_set.h | 2 +- ecmascript/js_api_tree_set_iterator.cpp | 2 +- ecmascript/js_api_tree_set_iterator.h | 2 +- ecmascript/js_api_vector.cpp | 9 +- ecmascript/js_api_vector.h | 3 +- ecmascript/js_hclass.h | 23 +- ecmascript/js_object-inl.h | 10 + ecmascript/js_object.h | 2 + ecmascript/js_tagged_value-inl.h | 20 + ecmascript/js_tagged_value.cpp | 107 ++- ecmascript/js_tagged_value.h | 4 + ecmascript/mem/object_xray.h | 16 + ecmascript/object_factory.cpp | 74 +- ecmascript/object_factory.h | 12 +- ecmascript/runtime_call_id.h | 47 +- .../snapshot/mem/snapshot_processor.cpp | 53 +- ecmascript/tagged_list.cpp | 684 ++++++++++++++++++ ecmascript/tagged_list.h | 190 +++++ ecmascript/tagged_tree.cpp | 2 +- ecmascript/tagged_tree.h | 2 +- ecmascript/tests/BUILD.gn | 2 + ecmascript/tests/dump_test.cpp | 58 ++ ecmascript/tests/js_api_arraylist_test.cpp | 3 +- ecmascript/tests/js_api_deque_test.cpp | 23 + ecmascript/tests/js_api_linked_list_test.cpp | 338 +++++++++ ecmascript/tests/js_api_list_test.cpp | 303 ++++++++ ecmascript/tests/js_api_plain_array_test.cpp | 25 + ecmascript/tests/js_api_queue_test.cpp | 5 +- ecmascript/tests/js_api_stack_test.cpp | 22 + ecmascript/tests/js_api_tree_map_test.cpp | 2 +- ecmascript/tests/js_api_tree_set_test.cpp | 2 +- ecmascript/tests/js_api_vector_test.cpp | 22 + ecmascript/tests/tagged_tree_test.cpp | 2 +- test/moduletest/container/BUILD.gn | 14 +- test/moduletest/container/container.js | 2 +- .../container/container_arraylist.js | 2 +- .../container/container_linked_list.js | 130 ++++ test/moduletest/container/container_list.js | 160 ++++ .../moduletest/container/container_treemap.js | 2 +- .../moduletest/container/container_treeset.js | 2 +- test/moduletest/container/expect_output.txt | 48 +- 87 files changed, 5580 insertions(+), 160 deletions(-) create mode 100644 ecmascript/containers/containers_linked_list.cpp create mode 100644 ecmascript/containers/containers_linked_list.h create mode 100644 ecmascript/containers/containers_list.cpp create mode 100644 ecmascript/containers/containers_list.h create mode 100644 ecmascript/containers/tests/containers_linked_list_test.cpp create mode 100644 ecmascript/containers/tests/containers_list_test.cpp create mode 100644 ecmascript/js_api_linked_list.cpp create mode 100644 ecmascript/js_api_linked_list.h create mode 100644 ecmascript/js_api_linked_list_iterator.cpp create mode 100644 ecmascript/js_api_linked_list_iterator.h create mode 100644 ecmascript/js_api_list.cpp create mode 100644 ecmascript/js_api_list.h create mode 100644 ecmascript/js_api_list_iterator.cpp create mode 100644 ecmascript/js_api_list_iterator.h create mode 100644 ecmascript/tagged_list.cpp create mode 100644 ecmascript/tagged_list.h create mode 100644 ecmascript/tests/js_api_linked_list_test.cpp create mode 100644 ecmascript/tests/js_api_list_test.cpp create mode 100644 test/moduletest/container/container_linked_list.js create mode 100644 test/moduletest/container/container_list.js diff --git a/BUILD.gn b/BUILD.gn index e9d50c92..ed3ef2a8 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -346,6 +346,8 @@ ecma_source = [ "ecmascript/builtins/builtins_weak_set.cpp", "ecmascript/containers/containers_arraylist.cpp", "ecmascript/containers/containers_deque.cpp", + "ecmascript/containers/containers_linked_list.cpp", + "ecmascript/containers/containers_list.cpp", "ecmascript/containers/containers_plainarray.cpp", "ecmascript/containers/containers_private.cpp", "ecmascript/containers/containers_queue.cpp", @@ -391,6 +393,10 @@ ecma_source = [ "ecmascript/js_api_arraylist_iterator.cpp", "ecmascript/js_api_deque.cpp", "ecmascript/js_api_deque_iterator.cpp", + "ecmascript/js_api_linked_list.cpp", + "ecmascript/js_api_linked_list_iterator.cpp", + "ecmascript/js_api_list.cpp", + "ecmascript/js_api_list_iterator.cpp", "ecmascript/js_api_plain_array.cpp", "ecmascript/js_api_plain_array_iterator.cpp", "ecmascript/js_api_queue.cpp", @@ -477,6 +483,7 @@ ecma_source = [ "ecmascript/regexp/regexp_parser_cache.cpp", "ecmascript/shared_mm/shared_mm.cpp", "ecmascript/tagged_dictionary.cpp", + "ecmascript/tagged_list.cpp", "ecmascript/tagged_tree.cpp", "ecmascript/template_string.cpp", "ecmascript/weak_vector.cpp", diff --git a/ecmascript/containers/containers_arraylist.cpp b/ecmascript/containers/containers_arraylist.cpp index 6e3ce2dc..e9fee8f0 100644 --- a/ecmascript/containers/containers_arraylist.cpp +++ b/ecmascript/containers/containers_arraylist.cpp @@ -76,10 +76,11 @@ JSTaggedValue ContainersArrayList::Insert(EcmaRuntimeCallInfo *argv) JSHandle value = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); - if (!index->IsNumber()) { + if (!index->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "index is not Integer", JSTaggedValue::Exception()); } JSAPIArrayList::Insert(thread, JSHandle::Cast(self), value, JSTaggedValue::ToUint32(thread, index)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue::Undefined(); } @@ -166,7 +167,7 @@ JSTaggedValue ContainersArrayList::IncreaseCapacityTo(EcmaRuntimeCallInfo *argv) } JSHandle newCapacity = GetCallArg(argv, 0); - if (!newCapacity->IsNumber()) { + if (!newCapacity->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "newCapacity is not Integer", JSTaggedValue::Exception()); } @@ -274,7 +275,7 @@ JSTaggedValue ContainersArrayList::RemoveByIndex(EcmaRuntimeCallInfo *argv) } JSHandle value = GetCallArg(argv, 0); - if (!value->IsNumber()) { + if (!value->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "index is not Integer", JSTaggedValue::Exception()); } @@ -318,7 +319,7 @@ JSTaggedValue ContainersArrayList::RemoveByRange(EcmaRuntimeCallInfo *argv) JSHandle startIndex = GetCallArg(argv, 0); JSHandle endIndex = GetCallArg(argv, 1); - if (!startIndex->IsNumber() || !endIndex->IsNumber()) { + if (!startIndex->IsInteger() || !endIndex->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "startIndex or endIndex is not Integer", JSTaggedValue::Exception()); } JSAPIArrayList::RemoveByRange(thread, JSHandle::Cast(self), startIndex, endIndex); @@ -382,7 +383,7 @@ JSTaggedValue ContainersArrayList::SubArrayList(EcmaRuntimeCallInfo *argv) JSHandle value1 = GetCallArg(argv, 0); JSHandle value2 = GetCallArg(argv, 1); - if (!value1->IsNumber() || !value2->IsNumber()) { + if (!value1->IsInteger() || !value2->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "startIndex or endIndex is not Integer", JSTaggedValue::Exception()); } JSHandle newArrayList = diff --git a/ecmascript/containers/containers_linked_list.cpp b/ecmascript/containers/containers_linked_list.cpp new file mode 100644 index 00000000..45b0fa9a --- /dev/null +++ b/ecmascript/containers/containers_linked_list.cpp @@ -0,0 +1,428 @@ +/* + * 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_linked_list.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_list.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersLinkedList::LinkedListConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Constructor); + [[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 linkedList = JSHandle::Cast(obj); + JSTaggedValue doubleList = TaggedDoubleList::Create(thread); + linkedList->SetDoubleList(thread, doubleList); + return linkedList.GetTaggedValue(); +} + +JSTaggedValue ContainersLinkedList::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Add); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSAPILinkedList::Add(thread, jsAPILinkedList, value); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersLinkedList::AddFirst(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, AddFirst); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSAPILinkedList::AddFirst(thread, jsAPILinkedList, value); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersLinkedList::GetFirst(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, GetFirst); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + return jsAPILinkedList->GetFirst(); +} + +JSTaggedValue ContainersLinkedList::GetLast(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, GetLast); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + return jsAPILinkedList->GetLast(); +} + +JSTaggedValue ContainersLinkedList::Length(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Length); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + return JSTaggedValue(jsAPILinkedList->Length()); +} + +JSTaggedValue ContainersLinkedList::Insert(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Insert); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 1); + JSHandle index = GetCallArg(argv, 0); + + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Exception()); + } + + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSTaggedValue result = + JSAPILinkedList::Insert(thread, jsAPILinkedList, value, index->GetInt()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; +} + +JSTaggedValue ContainersLinkedList::Clear(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Clear); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + jsAPILinkedList->Clear(thread); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersLinkedList::Clone(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Clone); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSHandle newLinkedList = JSAPILinkedList::Clone(thread, jsAPILinkedList); + return newLinkedList.GetTaggedValue(); +} + +JSTaggedValue ContainersLinkedList::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Has); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSHandle element = GetCallArg(argv, 0); + return GetTaggedBoolean(jsAPILinkedList->Has(element.GetTaggedValue())); +} + +JSTaggedValue ContainersLinkedList::Get(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Get); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSHandle index = GetCallArg(argv, 0); + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Exception()); + } + return jsAPILinkedList->Get(index->GetInt()); +} + +JSTaggedValue ContainersLinkedList::GetIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, GetIndexOf); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSHandle element = GetCallArg(argv, 0); + return jsAPILinkedList->GetIndexOf(element.GetTaggedValue()); +} + +JSTaggedValue ContainersLinkedList::GetLastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, GetLastIndexOf); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSHandle element(GetCallArg(argv, 0)); + return jsAPILinkedList->GetLastIndexOf(element.GetTaggedValue()); +} + +JSTaggedValue ContainersLinkedList::RemoveByIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, RemoveByIndex); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle index = GetCallArg(argv, 0); + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSTaggedValue nodeData = + JSAPILinkedList::RemoveByIndex(thread, jsAPILinkedList, index->GetInt()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return nodeData; +} + +JSTaggedValue ContainersLinkedList::Remove(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Remove); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle element = GetCallArg(argv, 0); + JSHandle jsAPILinkedList = JSHandle::Cast(self); + return jsAPILinkedList->Remove(thread, element.GetTaggedValue()); +} + +JSTaggedValue ContainersLinkedList::RemoveFirst(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, RemoveFirst); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSTaggedValue lastValue = JSAPILinkedList::RemoveFirst(thread, jsAPILinkedList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return lastValue; +} + +JSTaggedValue ContainersLinkedList::RemoveFirstFound(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, RemoveFirstFound); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle element = GetCallArg(argv, 0); + + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSTaggedValue result = JSAPILinkedList::RemoveFirstFound(thread, jsAPILinkedList, element.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; +} + +JSTaggedValue ContainersLinkedList::RemoveLast(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, RemoveLast); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSTaggedValue lastValue = JSAPILinkedList::RemoveLast(thread, jsAPILinkedList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return lastValue; +} + +JSTaggedValue ContainersLinkedList::RemoveLastFound(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, RemoveLastFound); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle element = GetCallArg(argv, 0); + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSTaggedValue result = JSAPILinkedList::RemoveLastFound(thread, jsAPILinkedList, element.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; +} + +JSTaggedValue ContainersLinkedList::Set(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, Set); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle index = GetCallArg(argv, 0); + JSHandle element = GetCallArg(argv, 1); + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + JSTaggedValue oldValue = + JSAPILinkedList::Set(thread, jsAPILinkedList, index->GetInt(), element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return oldValue; +} + +JSTaggedValue ContainersLinkedList::ConvertToArray(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, ConvertToArray); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + JSHandle jsAPILinkedList = JSHandle::Cast(self); + return JSAPILinkedList::ConvertToArray(thread, jsAPILinkedList); +} + +JSTaggedValue ContainersLinkedList::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, ForEach); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", JSTaggedValue::Exception()); + } + + JSHandle callbackFnHandle(GetCallArg(argv, 0)); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The first arg is not Callable", JSTaggedValue::Exception()); + } + + JSHandle thisArgHandle = GetCallArg(argv, 1); + JSHandle linkedList = JSHandle::Cast(thisHandle); + JSHandle doubleList(thread, linkedList->GetDoubleList()); + int length = linkedList->Length(); + + int index = 0; + const uint32_t argsLength = 3; // 3: «kValue, k, O» + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + while (index < length) { + JSTaggedValue value = doubleList->Get(index); + EcmaRuntimeCallInfo info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); + info.SetCallArg(value, JSTaggedValue(index), thisHandle.GetTaggedValue()); + JSTaggedValue funcResult = JSFunction::Call(&info); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + index++; + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersLinkedList::GetIteratorObj(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, LinkedList, GetIteratorObj); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSAPILinkedListIterator::CreateLinkedListIterator(thread, self); + return iter.GetTaggedValue(); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_linked_list.h b/ecmascript/containers/containers_linked_list.h new file mode 100644 index 00000000..bab0d27a --- /dev/null +++ b/ecmascript/containers/containers_linked_list.h @@ -0,0 +1,51 @@ +/* + * 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_LINKED_LIST_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_LINKED_LIST_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +class ContainersLinkedList : public base::BuiltinsBase { +public: + static JSTaggedValue LinkedListConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLast(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Insert(EcmaRuntimeCallInfo *argv); + static JSTaggedValue AddFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Clone(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Get(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetIndexOf(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLastIndexOf(EcmaRuntimeCallInfo *argv); + static JSTaggedValue RemoveByIndex(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Remove(EcmaRuntimeCallInfo *argv); + static JSTaggedValue RemoveFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue RemoveFirstFound(EcmaRuntimeCallInfo *argv); + static JSTaggedValue RemoveLast(EcmaRuntimeCallInfo *argv); + static JSTaggedValue RemoveLastFound(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Length(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ConvertToArray(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetIteratorObj(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_LINKED_LIST_H diff --git a/ecmascript/containers/containers_list.cpp b/ecmascript/containers/containers_list.cpp new file mode 100644 index 00000000..5fdfbc0a --- /dev/null +++ b/ecmascript/containers/containers_list.cpp @@ -0,0 +1,409 @@ +/* + * 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_list.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_api_list.h" +#include "ecmascript/js_api_list_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_list.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersList::ListConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Constructor); + [[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 list = JSHandle::Cast(obj); + + JSTaggedValue singleList = TaggedSingleList::Create(thread); + list->SetSingleList(thread, singleList); + + return list.GetTaggedValue(); +} + +JSTaggedValue ContainersList::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Add); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSHandle jSAPIList = JSHandle::Cast(self); + JSAPIList::Add(thread, jSAPIList, value); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersList::Insert(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Insert); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle value = GetCallArg(argv, 0); + JSHandle index = GetCallArg(argv, 1); + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "index is not integer", JSTaggedValue::Exception()); + } + JSHandle jSAPIList = JSHandle::Cast(self); + JSAPIList::Insert(thread, jSAPIList, value, index->GetInt()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersList::GetFirst(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, GetFirst); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jSAPIList = JSHandle::Cast(self); + return jSAPIList->GetFirst(); +} + +JSTaggedValue ContainersList::GetLast(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, GetLast); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jSAPIList = JSHandle::Cast(self); + return jSAPIList->GetLast(); +} + +JSTaggedValue ContainersList::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Has); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jSAPIList = JSHandle::Cast(self); + JSHandle element= GetCallArg(argv, 0); + return GetTaggedBoolean(jSAPIList->Has(element.GetTaggedValue())); +} + +JSTaggedValue ContainersList::IsEmpty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, IsEmpty); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jSAPIList = JSHandle::Cast(self); + return GetTaggedBoolean(jSAPIList->IsEmpty()); +} + +JSTaggedValue ContainersList::Get(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Get); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + JSHandle index = GetCallArg(argv, 0); + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "index is not integer", JSTaggedValue::Exception()); + } + return jsAPIList->Get(index->GetInt()); +} + +JSTaggedValue ContainersList::GetIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, GetIndexOf); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + JSHandle element = GetCallArg(argv, 0); + return jsAPIList->GetIndexOf(element.GetTaggedValue()); +} + +JSTaggedValue ContainersList::GetLastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, GetLastIndexOf); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + JSHandle element = GetCallArg(argv, 0); + return jsAPIList->GetLastIndexOf(element.GetTaggedValue()); +} + +JSTaggedValue ContainersList::Set(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Set); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle index = GetCallArg(argv, 0); + JSHandle element = GetCallArg(argv, 1); + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "index is not integer", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + JSTaggedValue oldValue = JSAPIList::Set(thread, jsAPIList, index->GetInt(), element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return oldValue; +} + +JSTaggedValue ContainersList::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, ForEach); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // get and check List object + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + + // get and check callback function + JSHandle callbackFnHandle(GetCallArg(argv, 0)); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The first arg is not Callable", JSTaggedValue::Exception()); + } + + // If thisArgHandle was supplied, let T be thisArgHandle; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + JSHandle list = JSHandle::Cast(thisHandle); + JSHandle singleList(thread, list->GetSingleList()); + int length = list->Length(); + + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + int index = 0; + const uint32_t argsLength = 3; // 3: «kValue, k, O» + while (index < length) { + JSTaggedValue value = singleList->Get(index); + EcmaRuntimeCallInfo info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); + info.SetCallArg(value, JSTaggedValue(index), thisHandle.GetTaggedValue()); + JSTaggedValue funcResult = JSFunction::Call(&info); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + index++; + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersList::Clear(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Clear); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle jsAPIList = JSHandle::Cast(self); + jsAPIList->Clear(thread); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersList::RemoveByIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, RemoveByIndex); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle index = GetCallArg(argv, 0); + if (!index->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "index is not integer", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + JSTaggedValue result = JSAPIList::RemoveByIndex(thread, jsAPIList, index->GetInt()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; +} + +JSTaggedValue ContainersList::Remove(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Remove); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle element = GetCallArg(argv, 0); + JSHandle jsAPIList = JSHandle::Cast(self); + return jsAPIList->Remove(thread, element.GetTaggedValue()); +} + +JSTaggedValue ContainersList::ReplaceAllElements(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, ReplaceAllElements); + JSHandle thisHandle = GetThis(argv); + 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); + return JSAPIList::ReplaceAllElements(thread, thisHandle, callbackFnHandle, thisArgHandle); +} + +JSTaggedValue ContainersList::Equal(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Equal); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsJSAPIList()) { + return JSTaggedValue::False(); + } + JSHandle handleObj = JSHandle::Cast(obj); + return jsAPIList->Equal(thread, handleObj); +} + +JSTaggedValue ContainersList::Sort(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Sort); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not IsJSAPIList", JSTaggedValue::Exception()); + } + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); + } + return JSAPIList::Sort(thread, self, callbackFnHandle); +} + +JSTaggedValue ContainersList::GetIteratorObj(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, GetIteratorObj); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSAPIListIterator::CreateListIterator(thread, self); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersList::ConvertToArray(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, ConvertToArray); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + return JSAPIList::ConvertToArray(thread, jsAPIList); +} + +JSTaggedValue ContainersList::GetSubList(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, GetSubList); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle fromIndex= GetCallArg(argv, 0); + + if (!fromIndex->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "index is not integer", JSTaggedValue::Exception()); + } + + JSHandle toIndex = GetCallArg(argv, 1); + + if (!toIndex->IsInteger()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "index is not integer", JSTaggedValue::Exception()); + } + + JSHandle jsAPIList = JSHandle::Cast(self); + JSTaggedValue newList = JSAPIList::GetSubList(thread, jsAPIList, fromIndex->GetInt(), toIndex->GetInt()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return newList; +} + +JSTaggedValue ContainersList::Length(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv != nullptr); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, List, Length); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", JSTaggedValue::Exception()); + } + JSHandle jsAPIList = JSHandle::Cast(self); + return JSTaggedValue(jsAPIList->Length()); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_list.h b/ecmascript/containers/containers_list.h new file mode 100644 index 00000000..7375de14 --- /dev/null +++ b/ecmascript/containers/containers_list.h @@ -0,0 +1,49 @@ +/* + * 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_LIST_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_LIST_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +class ContainersList : public base::BuiltinsBase { +public: + static JSTaggedValue ListConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLast(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Insert(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + static JSTaggedValue RemoveByIndex(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Remove(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + static JSTaggedValue IsEmpty(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Get(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetIndexOf(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetLastIndexOf(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ReplaceAllElements(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetIteratorObj(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Equal(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Sort(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ConvertToArray(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetSubList(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Length(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_LIST_H diff --git a/ecmascript/containers/containers_plainarray.cpp b/ecmascript/containers/containers_plainarray.cpp index 209a3d8d..a84aef53 100644 --- a/ecmascript/containers/containers_plainarray.cpp +++ b/ecmascript/containers/containers_plainarray.cpp @@ -57,7 +57,7 @@ JSTaggedValue ContainersPlainArray::Add(EcmaRuntimeCallInfo *argv) } JSHandle key(GetCallArg(argv, 0)); JSHandle value(GetCallArg(argv, 1)); - if (!key->IsNumber()) { + if (!key->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Exception()); } JSAPIPlainArray::Add(thread, JSHandle::Cast(self), key, value); @@ -125,7 +125,7 @@ JSTaggedValue ContainersPlainArray::Get(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIPlainArray", JSTaggedValue::Exception()); } JSHandle key(GetCallArg(argv, 0)); - if (!key->IsNumber()) { + if (!key->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "the key is not integer", JSTaggedValue::Exception()); } JSAPIPlainArray *array = JSAPIPlainArray::Cast(self->GetTaggedObject()); @@ -253,7 +253,7 @@ JSTaggedValue ContainersPlainArray::Remove(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIPlainArray", JSTaggedValue::Exception()); } JSHandle key(GetCallArg(argv, 0)); - if (!key->IsNumber()) { + if (!key->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "the key is not integer", JSTaggedValue::Undefined()); } JSAPIPlainArray *array = JSAPIPlainArray::Cast(self->GetTaggedObject()); @@ -271,7 +271,7 @@ JSTaggedValue ContainersPlainArray::RemoveAt(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIPlainArray", JSTaggedValue::Exception()); } JSHandle index(GetCallArg(argv, 0)); - if (!index->IsNumber()) { + if (!index->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Undefined()); } JSAPIPlainArray *array = JSAPIPlainArray::Cast(self->GetTaggedObject()); @@ -290,7 +290,7 @@ JSTaggedValue ContainersPlainArray::RemoveRangeFrom(EcmaRuntimeCallInfo *argv) } JSHandle valueIndex(GetCallArg(argv, 0)); JSHandle valueSize(GetCallArg(argv, 1)); - if (!valueIndex->IsNumber() || !valueSize->IsNumber()) { + if (!valueIndex->IsInteger() || !valueSize->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "the index or the value is not integer", JSTaggedValue::Undefined()); } int32_t index = valueIndex->GetNumber(); @@ -312,7 +312,7 @@ JSTaggedValue ContainersPlainArray::SetValueAt(EcmaRuntimeCallInfo *argv) } JSHandle index(GetCallArg(argv, 0)); JSHandle value(GetCallArg(argv, 1)); - if (!index->IsNumber()) { + if (!index->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Exception()); } JSAPIPlainArray *array = JSAPIPlainArray::Cast(self->GetTaggedObject()); @@ -331,7 +331,7 @@ JSTaggedValue ContainersPlainArray::GetValueAt(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIPlainArray", JSTaggedValue::Exception()); } JSHandle idx(GetCallArg(argv, 0)); - if (!idx->IsNumber()) { + if (!idx->IsInteger()) { THROW_TYPE_ERROR_AND_RETURN(thread, "the index is not integer", JSTaggedValue::Undefined()); } JSAPIPlainArray *array = JSAPIPlainArray::Cast(self->GetTaggedObject()); diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp index 3d620424..f599f347 100644 --- a/ecmascript/containers/containers_private.cpp +++ b/ecmascript/containers/containers_private.cpp @@ -17,6 +17,8 @@ #include "containers_arraylist.h" #include "containers_deque.h" +#include "containers_linked_list.h" +#include "containers_list.h" #include "containers_plainarray.h" #include "containers_queue.h" #include "containers_stack.h" @@ -30,6 +32,10 @@ #include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_api_deque.h" #include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_api_list.h" +#include "ecmascript/js_api_list_iterator.h" #include "ecmascript/js_api_plain_array.h" #include "ecmascript/js_api_plain_array_iterator.h" #include "ecmascript/js_api_queue.h" @@ -92,8 +98,14 @@ JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) res = InitializeContainer(thread, thisValue, InitializeVector, "VectorConstructor"); break; } - case ContainerTag::List: - case ContainerTag::LinkedList: + case ContainerTag::List: { + res = InitializeContainer(thread, thisValue, InitializeList, "ListConstructor"); + break; + } + case ContainerTag::LinkedList: { + res = InitializeContainer(thread, thisValue, InitializeLinkedList, "LinkedListConstructor"); + break; + } case ContainerTag::HashMap: case ContainerTag::HashSet: case ContainerTag::LightWeightMap: @@ -237,7 +249,8 @@ JSHandle ContainersPrivate::InitializeArrayList(JSThread *thread) // ArrayList() = new Function() JSHandle arrayListFunction(NewContainerConstructor( thread, prototype, ContainersArrayList::ArrayListConstructor, "ArrayList", FuncLength::ZERO)); - JSHandle(arrayListFunction)->SetFunctionPrototype(thread, arrayListInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(arrayListFunction)->SetFunctionPrototype(thread, + arrayListInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); @@ -311,7 +324,7 @@ JSHandle ContainersPrivate::InitializeTreeMap(JSThread *thread) // TreeMap() = new Function() JSHandle mapFunction(NewContainerConstructor( thread, mapFuncPrototype, ContainersTreeMap::TreeMapConstructor, "TreeMap", FuncLength::ZERO)); - JSHandle(mapFunction)->SetFunctionPrototype(thread, mapInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(mapFunction)->SetFunctionPrototype(thread, mapInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); @@ -386,7 +399,7 @@ JSHandle ContainersPrivate::InitializeTreeSet(JSThread *thread) // TreeSet() = new Function() JSHandle setFunction(NewContainerConstructor( thread, setFuncPrototype, ContainersTreeSet::TreeSetConstructor, "TreeSet", FuncLength::ZERO)); - JSHandle(setFunction)->SetFunctionPrototype(thread, setInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(setFunction)->SetFunctionPrototype(thread, setInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); @@ -458,8 +471,9 @@ JSHandle ContainersPrivate::InitializePlainArray(JSThread *thread JSHandle plainArrayFunction( NewContainerConstructor(thread, plainArrayFuncPrototype, ContainersPlainArray::PlainArrayConstructor, "PlainArray", FuncLength::ZERO)); - JSHandle(plainArrayFunction)->SetFunctionPrototype(thread, - plainArrayInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(plainArrayFunction)->SetFunctionPrototype(thread, + plainArrayInstanceDynclass.GetTaggedValue()); + // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread, JSHandle(plainArrayFuncPrototype), constructorKey, @@ -531,7 +545,7 @@ JSHandle ContainersPrivate::InitializeStack(JSThread *thread) // Stack() = new Function() JSHandle stackFunction(NewContainerConstructor( thread, stackFuncPrototype, ContainersStack::StackConstructor, "Stack", FuncLength::ZERO)); - JSHandle(stackFunction)->SetFunctionPrototype(thread, stackInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(stackFunction)->SetFunctionPrototype(thread, stackInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); @@ -589,7 +603,7 @@ JSHandle ContainersPrivate::InitializeVector(JSThread *thread) // Vector() = new Function() JSHandle vectorFunction(NewContainerConstructor( thread, prototype, ContainersVector::VectorConstructor, "Vector", FuncLength::ZERO)); - JSHandle(vectorFunction)->SetFunctionPrototype(thread, vectorInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(vectorFunction)->SetFunctionPrototype(thread, vectorInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); @@ -668,7 +682,7 @@ JSHandle ContainersPrivate::InitializeQueue(JSThread *thread) // Queue() = new Function() JSHandle queueFunction(NewContainerConstructor( thread, queueFuncPrototype, ContainersQueue::QueueConstructor, "Queue", FuncLength::ZERO)); - JSHandle(queueFunction)->SetFunctionPrototype(thread, queueInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(queueFunction)->SetFunctionPrototype(thread, queueInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); @@ -721,7 +735,7 @@ JSHandle ContainersPrivate::InitializeDeque(JSThread *thread) // Deque() = new Function() JSHandle dequeFunction(NewContainerConstructor( thread, dequeFuncPrototype, ContainersDeque::DequeConstructor, "Deque", FuncLength::ZERO)); - JSHandle(dequeFunction)->SetFunctionPrototype(thread, dequeInstanceDynclass.GetTaggedValue()); + JSHandle::Cast(dequeFunction)->SetFunctionPrototype(thread, dequeInstanceDynclass.GetTaggedValue()); // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); @@ -762,4 +776,135 @@ void ContainersPrivate::InitializeDequeIterator(JSThread *thread, const JSHandle SetStringTagSymbol(thread, env, dequeIteratorPrototype, "Deque Iterator"); globalConst->SetConstant(ConstantIndex::DEQUE_ITERATOR_PROTOTYPE_INDEX, dequeIteratorPrototype.GetTaggedValue()); } + +JSHandle ContainersPrivate::InitializeList(JSThread *thread) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle listFuncPrototype = factory->NewEmptyJSObject(); + JSHandle listFuncPrototypeValue(listFuncPrototype); + JSHandle listInstanceDynclass = + factory->NewEcmaDynClass(JSAPIList::SIZE, JSType::JS_API_LIST, listFuncPrototypeValue); + JSHandle listFunction(NewContainerConstructor( + thread, listFuncPrototype, ContainersList::ListConstructor, "List", FuncLength::ZERO)); + JSHandle::Cast(listFunction)->SetFunctionPrototype(thread, listInstanceDynclass.GetTaggedValue()); + + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(listFuncPrototype), constructorKey, listFunction); + + SetFrozenFunction(thread, listFuncPrototype, "add", ContainersList::Add, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "getFirst", ContainersList::GetFirst, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "getLast", ContainersList::GetLast, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "insert", ContainersList::Insert, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "clear", ContainersList::Clear, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "removeByIndex", ContainersList::RemoveByIndex, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "remove", ContainersList::Remove, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "has", ContainersList::Has, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "isEmpty", ContainersList::IsEmpty, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "get", ContainersList::Get, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "getIndexOf", ContainersList::GetIndexOf, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "getLastIndexOf", ContainersList::GetLastIndexOf, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "set", ContainersList::Set, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "forEach", ContainersList::ForEach, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "replaceAllElements", ContainersList::ReplaceAllElements, + FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "equal", ContainersList::Equal, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "sort", ContainersList::Sort, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "convertToArray", ContainersList::ConvertToArray, FuncLength::ONE); + SetFrozenFunction(thread, listFuncPrototype, "getSubList", ContainersList::GetSubList, FuncLength::ONE); + + JSHandle lengthGetter = CreateGetter(thread, ContainersList::Length, "length", FuncLength::ZERO); + JSHandle lengthKey(factory->NewFromASCII("length")); + SetGetter(thread, listFuncPrototype, lengthKey, lengthGetter); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + SetFunctionAtSymbol(thread, env, listFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + ContainersList::GetIteratorObj, FuncLength::ONE); + + InitializeListIterator(thread, env); + globalConst->SetConstant(ConstantIndex::LIST_FUNCTION_INDEX, listFunction.GetTaggedValue()); + return listFunction; +} + +JSHandle ContainersPrivate::InitializeLinkedList(JSThread *thread) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle linkedListFuncPrototype = factory->NewEmptyJSObject(); + JSHandle linkedListFuncPrototypeValue(linkedListFuncPrototype); + JSHandle linkedListInstanceDynclass = + factory->NewEcmaDynClass(JSAPILinkedList::SIZE, JSType::JS_API_LINKED_LIST, linkedListFuncPrototypeValue); + JSHandle linkedListFunction(NewContainerConstructor( + thread, linkedListFuncPrototype, ContainersLinkedList::LinkedListConstructor, "LinkedList", FuncLength::ZERO)); + JSHandle::Cast(linkedListFunction)->SetFunctionPrototype(thread, + linkedListInstanceDynclass.GetTaggedValue()); + + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(linkedListFuncPrototype), constructorKey, linkedListFunction); + + SetFrozenFunction(thread, linkedListFuncPrototype, "add", ContainersLinkedList::Add, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "insert", ContainersLinkedList::Insert, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "clear", ContainersLinkedList::Clear, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "clone", ContainersLinkedList::Clone, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "removeFirst", ContainersLinkedList::RemoveFirst, + FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "removeLast", ContainersLinkedList::RemoveLast, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "removeFirstFound", ContainersLinkedList::RemoveFirstFound, + FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "removeByIndex", ContainersLinkedList::RemoveByIndex, + FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "remove", ContainersLinkedList::Remove, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "removeLastFound", ContainersLinkedList::RemoveLastFound, + FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "has", ContainersLinkedList::Has, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "get", ContainersLinkedList::Get, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "addFirst", ContainersLinkedList::AddFirst, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "getFirst", ContainersLinkedList::GetFirst, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "getLast", ContainersLinkedList::GetLast, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "getIndexOf", ContainersLinkedList::GetIndexOf, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "getLastIndexOf", ContainersLinkedList::GetLastIndexOf, + FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "convertToArray", ContainersLinkedList::ConvertToArray, + FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "set", ContainersLinkedList::Set, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "forEach", ContainersLinkedList::ForEach, FuncLength::ONE); + + JSHandle lengthGetter = CreateGetter(thread, ContainersLinkedList::Length, "length", + FuncLength::ZERO); + JSHandle lengthKey(factory->NewFromASCII("length")); + SetGetter(thread, linkedListFuncPrototype, lengthKey, lengthGetter); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + SetFunctionAtSymbol(thread, env, linkedListFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + ContainersLinkedList::GetIteratorObj, FuncLength::ONE); + + InitializeLinkedListIterator(thread, env); + globalConst->SetConstant(ConstantIndex::LINKED_LIST_FUNCTION_INDEX, linkedListFunction.GetTaggedValue()); + return linkedListFunction; +} + +void ContainersPrivate::InitializeLinkedListIterator(JSThread *thread, const JSHandle &env) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iteratorDynclass = + JSHandle(thread, globalConst->GetHandledJSAPIIteratorFuncDynClass().GetObject()); + JSHandle setIteratorPrototype(factory->NewJSObject(iteratorDynclass)); + SetFrozenFunction(thread, setIteratorPrototype, "next", JSAPILinkedListIterator::Next, FuncLength::ONE); + SetStringTagSymbol(thread, env, setIteratorPrototype, "linkedlist Iterator"); + globalConst->SetConstant(ConstantIndex::LINKED_LIST_ITERATOR_PROTOTYPE_INDEX, + setIteratorPrototype.GetTaggedValue()); +} + +void ContainersPrivate::InitializeListIterator(JSThread *thread, const JSHandle &env) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iteratorDynclass = + JSHandle(thread, globalConst->GetHandledJSAPIIteratorFuncDynClass().GetObject()); + JSHandle setIteratorPrototype(factory->NewJSObject(iteratorDynclass)); + SetFrozenFunction(thread, setIteratorPrototype, "next", JSAPIListIterator::Next, FuncLength::ONE); + SetStringTagSymbol(thread, env, setIteratorPrototype, "list Iterator"); + globalConst->SetConstant(ConstantIndex::LIST_ITERATOR_PROTOTYPE_INDEX, setIteratorPrototype.GetTaggedValue()); +} } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_private.h b/ecmascript/containers/containers_private.h index cdaf2988..c3e27ef8 100644 --- a/ecmascript/containers/containers_private.h +++ b/ecmascript/containers/containers_private.h @@ -87,6 +87,10 @@ private: GlobalEnvConstants *globalConst); static JSHandle InitializeStack(JSThread *thread); static void InitializeStackIterator(JSThread *thread, GlobalEnvConstants *globalConst); + static JSHandle InitializeList(JSThread *thread); + static JSHandle InitializeLinkedList(JSThread *thread); + static void InitializeLinkedListIterator(JSThread *thread, const JSHandle &env); + static void InitializeListIterator(JSThread *thread, const JSHandle &env); }; } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/tests/BUILD.gn b/ecmascript/containers/tests/BUILD.gn index 908f7e90..54ff4c5e 100644 --- a/ecmascript/containers/tests/BUILD.gn +++ b/ecmascript/containers/tests/BUILD.gn @@ -23,6 +23,8 @@ host_unittest_action("ContainersTest") { sources = [ # test file "containers_deque_test.cpp", + "containers_linked_list_test.cpp", + "containers_list_test.cpp", "containers_plainarray_test.cpp", "containers_stack_test.cpp", "containers_treemap_test.cpp", diff --git a/ecmascript/containers/tests/containers_linked_list_test.cpp b/ecmascript/containers/tests/containers_linked_list_test.cpp new file mode 100644 index 00000000..0bc05130 --- /dev/null +++ b/ecmascript/containers/tests/containers_linked_list_test.cpp @@ -0,0 +1,453 @@ +/* + * 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_linked_list.h" +#include "ecmascript/containers/containers_private.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_linked_list_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 ContainersLinkedListTest : 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); + } + + EcmaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSThread *thread = argv->GetThread(); + JSHandle value = GetCallArg(argv, 0); + JSHandle index = GetCallArg(argv, 1); + JSHandle list = GetCallArg(argv, 2); // 2 means the secode arg + if (!list->IsUndefined()) { + if (index->IsNumber() && value->IsNumber()) { + JSHandle newValue(thread, JSTaggedValue(value->GetInt() * 2)); // 2 means mul by 2 + JSAPILinkedList::Set(thread, JSHandle::Cast(list), index->GetInt(), newValue); + } + } + return JSTaggedValue::True(); + } + }; +protected: + JSTaggedValue InitializeLinkedListConstructor() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromASCII("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(ContainerTag::LinkedList))); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersPrivate::Load(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + return result; + } + + JSHandle CreateJSAPILinkedList(JSTaggedValue compare = JSTaggedValue::Undefined()) + { + JSHandle compareHandle(thread, compare); + JSHandle newTarget(thread, InitializeLinkedListConstructor()); + 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 = ContainersLinkedList::LinkedListConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle linkedlist(thread, result); + return linkedlist; + } +}; + +HWTEST_F_L0(ContainersLinkedListTest, LinkedListConstructor) +{ + InitializeLinkedListConstructor(); + JSHandle newTarget(thread, InitializeLinkedListConstructor()); + + 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 = ContainersLinkedList::LinkedListConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsJSAPILinkedList()); + JSHandle list(thread, result); + JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle(list)); + JSTaggedValue funcProto = newTarget->GetFunctionPrototype(); + ASSERT_EQ(resultProto, funcProto); + int size = list->Length(); + ASSERT_EQ(size, 0); +} + +HWTEST_F_L0(ContainersLinkedListTest, InsertAndGet) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle linkedlist = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Insert(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(i + 1)); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Get(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersLinkedListTest, Remove) +{ + constexpr uint32_t NODE_NUMBERS = 20; + JSHandle linkedlist = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Insert(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(NODE_NUMBERS / 2)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersLinkedList::Remove(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(NODE_NUMBERS - 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(6)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersLinkedList::RemoveByIndex(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue(6)); + EXPECT_EQ(linkedlist->Length(), static_cast(NODE_NUMBERS - 2)); + } +} + +HWTEST_F_L0(ContainersLinkedListTest, RemoveFirst) +{ + constexpr uint32_t NODE_NUMBERS = 20; + JSHandle linkedlist = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Insert(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersLinkedList::RemoveFirst(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue(0)); + EXPECT_EQ(linkedlist->Length(), static_cast(NODE_NUMBERS - 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(15)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersLinkedList::RemoveFirstFound(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(NODE_NUMBERS - 2)); + } +} + +HWTEST_F_L0(ContainersLinkedListTest, RemoveLast) +{ + constexpr uint32_t NODE_NUMBERS = 20; + JSHandle linkedlist = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Insert(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersLinkedList::RemoveLast(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue(19)); + EXPECT_EQ(linkedlist->Length(), static_cast(NODE_NUMBERS - 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(8)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersLinkedList::RemoveLastFound(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(NODE_NUMBERS - 2)); + } +} + +HWTEST_F_L0(ContainersLinkedListTest, Clear) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle linkedlist = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersLinkedList::Clear(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(linkedlist->Length(), 0); + } +} + +HWTEST_F_L0(ContainersLinkedListTest, Clone) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle linkedList = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedList.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedList->Length(), static_cast(i + 1)); + } + + linkedList->Dump(); + + auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo1->SetFunction(JSTaggedValue::Undefined()); + callInfo1->SetThis(linkedList.GetTaggedValue()); + JSTaggedValue newlinkedList = ContainersLinkedList::Clone(callInfo1.get()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(newlinkedList); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Get(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersLinkedListTest, Values) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle linkedlist = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(i + 1)); + } + + // test values + auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo1->SetFunction(JSTaggedValue::Undefined()); + callInfo1->SetThis(linkedlist.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo1.get()); + JSHandle iterValues(thread, ContainersLinkedList::GetIteratorObj(callInfo1.get())); + TestHelper::TearDownFrame(thread, prev1); + EXPECT_TRUE(iterValues->IsJSAPILinkedListIterator()); + + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(iterValues.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + result.Update(JSAPILinkedListIterator::Next(callInfo.get())); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(static_cast(i), JSIterator::IteratorValue(thread, result)->GetInt()); + } +} + +HWTEST_F_L0(ContainersLinkedListTest, ForEach) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle linkedlist = CreateJSAPILinkedList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(linkedlist->Length(), static_cast(i + 1)); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newLinkedlist = CreateJSAPILinkedList(); + { + 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(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, func.GetTaggedValue()); + callInfo->SetCallArg(1, newLinkedlist.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersLinkedList::ForEach(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(linkedlist.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersLinkedList::Get(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(i * 2)); + } +} +} // namespace panda::test diff --git a/ecmascript/containers/tests/containers_list_test.cpp b/ecmascript/containers/tests/containers_list_test.cpp new file mode 100644 index 00000000..e6ebce02 --- /dev/null +++ b/ecmascript/containers/tests/containers_list_test.cpp @@ -0,0 +1,461 @@ +/* + * 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_list.h" +#include "ecmascript/containers/containers_private.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_api_list.h" +#include "ecmascript/js_api_list_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 ContainersListTest : 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); + } + + EcmaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSThread *thread = argv->GetThread(); + JSHandle value = GetCallArg(argv, 0); + JSHandle index = GetCallArg(argv, 1); + JSHandle list = GetCallArg(argv, 2); // 2 means the secode arg + if (!list->IsUndefined()) { + if (index->IsNumber() && value->IsNumber()) { + JSHandle newValue(thread, JSTaggedValue(value->GetInt() * 2)); // 2 means mul by 2 + JSAPIList::Set(thread, JSHandle::Cast(list), index->GetInt(), newValue); + } + } + return JSTaggedValue::True(); + } + }; +protected: + JSTaggedValue InitializeListConstructor() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromASCII("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(ContainerTag::List))); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = ContainersPrivate::Load(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + return result; + } + + JSHandle CreateJSAPIList(JSTaggedValue compare = JSTaggedValue::Undefined()) + { + JSHandle compareHandle(thread, compare); + JSHandle newTarget(thread, InitializeListConstructor()); + 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 = ContainersList::ListConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle list(thread, result); + return list; + } +}; + +HWTEST_F_L0(ContainersListTest, ListConstructor) +{ + InitializeListConstructor(); + JSHandle newTarget(thread, InitializeListConstructor()); + + 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 = ContainersList::ListConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsJSAPIList()); + JSHandle list(thread, result); + JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle(list)); + JSTaggedValue funcProto = newTarget->GetFunctionPrototype(); + ASSERT_EQ(resultProto, funcProto); + int size = list->Length(); + ASSERT_EQ(size, 0); +} + +HWTEST_F_L0(ContainersListTest, InsertAndGet) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle list = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Insert(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(20)); + callInfo->SetCallArg(1, JSTaggedValue(20)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Insert(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::Exception()); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Get(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(i)); + } +} + +HWTEST_F_L0(ContainersListTest, Remove) +{ + constexpr uint32_t NODE_NUMBERS = 20; + JSHandle list = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Insert(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(NODE_NUMBERS / 2)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersList::Remove(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(NODE_NUMBERS - 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(6)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue rvalue = ContainersList::RemoveByIndex(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(rvalue, JSTaggedValue(6)); + EXPECT_EQ(list->Length(), static_cast(NODE_NUMBERS - 2)); + } +} + +HWTEST_F_L0(ContainersListTest, Equal) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle list = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + + JSHandle list1 = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list1.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list1->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, list1.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue res = ContainersList::Equal(callInfo.get()); + EXPECT_EQ(res, JSTaggedValue::True()); + TestHelper::TearDownFrame(thread, prev); + } +} + +HWTEST_F_L0(ContainersListTest, GetSubList) +{ + constexpr uint32_t NODE_NUMBERS = 10; + JSHandle list = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + + { + auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo1->SetFunction(JSTaggedValue::Undefined()); + callInfo1->SetThis(list.GetTaggedValue()); + callInfo1->SetCallArg(0, JSTaggedValue(2)); + callInfo1->SetCallArg(1, JSTaggedValue(5)); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo1.get()); + JSTaggedValue newList = ContainersList::GetSubList(callInfo1.get()); + TestHelper::TearDownFrame(thread, prev1); + EXPECT_EQ(list->Length(), 10); + for (uint32_t i = 0; i < 3; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(newList); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Get(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(i + 2)); + } + } +} + +HWTEST_F_L0(ContainersListTest, ConvertToArray) +{ + constexpr uint32_t NODE_NUMBERS = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle list = CreateJSAPIList(); + JSHandle oldArray(factory->NewTaggedArray(NODE_NUMBERS, JSTaggedValue::Hole())); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Add(callInfo.get()); + oldArray->Set(thread, i, JSTaggedValue(i)); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue newArray = ContainersList::ConvertToArray(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue newArrayValue = + JSTaggedValue::ToObject(thread, JSHandle(thread, newArray))->GetElements(); + JSHandle newArrayHandle(thread, newArrayValue); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + EXPECT_EQ(newArrayHandle->Get(i), oldArray->Get(i)); + } +} + +HWTEST_F_L0(ContainersListTest, Clear) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle list = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + + { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersList::Clear(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(list->Length(), 0); + } +} + +HWTEST_F_L0(ContainersListTest, Values) +{ + constexpr int NODE_NUMBERS = 8; + JSHandle list = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + + auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo1->SetFunction(JSTaggedValue::Undefined()); + callInfo1->SetThis(list.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo1.get()); + JSHandle iterValues(thread, ContainersList::GetIteratorObj(callInfo1.get())); + TestHelper::TearDownFrame(thread, prev1); + EXPECT_TRUE(iterValues->IsJSAPIListIterator()); + + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + for (int i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(iterValues.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + result.Update(JSAPIListIterator::Next(callInfo.get())); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(i, JSIterator::IteratorValue(thread, result)->GetInt()); + } +} + +HWTEST_F_L0(ContainersListTest, ForEach) +{ + constexpr uint32_t NODE_NUMBERS = 8; + JSHandle list = CreateJSAPIList(); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + callInfo->SetCallArg(1, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Add(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue::True()); + EXPECT_EQ(list->Length(), static_cast(i + 1)); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dlist = CreateJSAPIList(); + { + 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(list.GetTaggedValue()); + callInfo->SetCallArg(0, func.GetTaggedValue()); + callInfo->SetCallArg(1, dlist.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + ContainersList::ForEach(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + callInfo->SetFunction(JSTaggedValue::Undefined()); + callInfo->SetThis(list.GetTaggedValue()); + callInfo->SetCallArg(0, JSTaggedValue(i)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo.get()); + JSTaggedValue result = ContainersList::Get(callInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result, JSTaggedValue(i * 2)); + } +} +} // namespace panda::test diff --git a/ecmascript/dfx/hprof/heap_snapshot.cpp b/ecmascript/dfx/hprof/heap_snapshot.cpp index 11b38e33..d74c7b81 100644 --- a/ecmascript/dfx/hprof/heap_snapshot.cpp +++ b/ecmascript/dfx/hprof/heap_snapshot.cpp @@ -361,6 +361,10 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) return GetString("Stack"); case JSType::JS_API_STACK_ITERATOR: return GetString("StackIterator"); + case JSType::JS_API_LIST: + return GetString("List"); + case JSType::JS_API_LINKED_LIST: + return GetString("LinkedList"); case JSType::SOURCE_TEXT_MODULE_RECORD: return GetString("SourceTextModule"); case JSType::IMPORTENTRY_RECORD: diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index be4944b0..3411f630 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -34,6 +34,10 @@ #include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_api_deque.h" #include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_api_list.h" +#include "ecmascript/js_api_list_iterator.h" #include "ecmascript/js_api_plain_array.h" #include "ecmascript/js_api_plain_array_iterator.h" #include "ecmascript/js_api_queue.h" @@ -93,6 +97,7 @@ #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/tagged_array.h" #include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_list.h" #include "ecmascript/tagged_tree.h" #include "ecmascript/template_map.h" #include "ecmascript/transitions_dictionary.h" @@ -341,6 +346,14 @@ CString JSHClass::DumpJSType(JSType type) return "Stack"; case JSType::JS_API_STACK_ITERATOR: return "StackIterator"; + case JSType::JS_API_LIST: + return "List"; + case JSType::JS_API_LIST_ITERATOR: + return "ListIterator"; + case JSType::JS_API_LINKED_LIST: + return "LinkedList"; + case JSType::JS_API_LINKED_LIST_ITERATOR: + return "LinkedListIterator"; default: { CString ret = "unknown type "; return ret + static_cast(type); @@ -764,6 +777,18 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::JS_API_STACK_ITERATOR: JSAPIStackIterator::Cast(obj)->Dump(os); break; + case JSType::JS_API_LIST: + JSAPIList::Cast(obj)->Dump(os); + break; + case JSType::JS_API_LIST_ITERATOR: + JSAPIListIterator::Cast(obj)->Dump(os); + break; + case JSType::JS_API_LINKED_LIST: + JSAPILinkedList::Cast(obj)->Dump(os); + break; + case JSType::JS_API_LINKED_LIST_ITERATOR: + JSAPILinkedListIterator::Cast(obj)->Dump(os); + break; case JSType::SOURCE_TEXT_MODULE_RECORD: SourceTextModule::Cast(obj)->Dump(os); break; @@ -1007,6 +1032,58 @@ void LinkedHashMap::Dump(std::ostream &os) const } } +void TaggedDoubleList::Dump(std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfNodes(); + os << " - node num: " << std::dec << capacity << "\n"; + os << " - delete node num: " << std::dec << NumberOfDeletedNodes() << "\n"; + os << "head-next: "; + // 5 : 5 first element next ptr + GetElement(5).D(); + os << "head-pre: "; + // 6 : 6 first element per ptr + GetElement(6).D(); + os << "\n"; + int i = 0; + int next = GetElement(5).GetInt(); + while (capacity > i) { + os << " value: "; + GetElement(next).DumpTaggedValue(os); + os << " next: "; + // 1 : 1 current element next ptr offset + GetElement(next + 1).D(); + os << " pre: "; + // 2 : 2 current element pre ptr offset + GetElement(next + 2).D(); + os << "\n"; + next = GetElement(next + 1).GetInt(); + i++; + } +} + +void TaggedSingleList::Dump(std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfNodes(); + os << "head-next: "; + // 5 : 5 first element next ptr + GetElement(5).D(); + os << "\n"; + int i = 0; + int next = GetElement(5).GetInt(); + while (capacity > i) { + os << " value: "; + GetElement(next).DumpTaggedValue(os); + os << " next: "; + // 1 : 1 current element next ptr offset + GetElement(next + 1).D(); + os << "\n"; + next = GetElement(next + 1).GetInt(); + i++; + } +} + void JSObject::Dump(std::ostream &os) const { DISALLOW_GARBAGE_COLLECTION; @@ -1561,14 +1638,14 @@ void JSArray::Dump(std::ostream &os) const void JSAPIArrayList::Dump(std::ostream &os) const { - os << " - length: " << std::dec << GetLength().GetArrayLength() << "\n"; + os << " - length: " << std::dec << GetSize() << "\n"; JSObject::Dump(os); } void JSAPIArrayListIterator::Dump(std::ostream &os) const { JSAPIArrayList *arrayList = JSAPIArrayList::Cast(GetIteratedArrayList().GetTaggedObject()); - os << " - length: " << std::dec << arrayList->GetLength().GetArrayLength() << "\n"; + os << " - length: " << std::dec << arrayList->GetSize() << "\n"; os << " - nextIndex: " << std::dec << GetNextIndex() << "\n"; JSObject::Dump(os); } @@ -1611,6 +1688,80 @@ void JSArrayIterator::Dump(std::ostream &os) const JSObject::Dump(os); } +void JSAPIList::Dump(std::ostream &os) const +{ + TaggedSingleList *list = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + os << " - length: " << std::dec << list->GetCapacityFromTaggedArray() << "\n"; + os << " - node num: " << std::dec << list->NumberOfNodes() << "\n"; + os << " - delete node num: " << std::dec << list->NumberOfDeletedNodes() << "\n"; + JSObject::Dump(os); + list->Dump(os); +} + +void JSAPIList::DumpForSnapshot(std::vector> &vec) const +{ + TaggedSingleList *map = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + map->DumpForSnapshot(vec); + + JSObject::DumpForSnapshot(vec); +} + +void JSAPIListIterator::Dump(std::ostream &os) const +{ + TaggedSingleList *list = TaggedSingleList::Cast(GetIteratedList().GetTaggedObject()); + os << " - length: " << std::dec << list->GetCapacityFromTaggedArray() << "\n"; + os << " - node num: " << std::dec << list->NumberOfNodes() << "\n"; + os << " - delete node num: " << std::dec << list->NumberOfDeletedNodes() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex() << "\n"; + JSObject::Dump(os); + list->Dump(os); +} + +void JSAPIListIterator::DumpForSnapshot(std::vector> &vec) const +{ + TaggedSingleList *list = TaggedSingleList::Cast(GetIteratedList().GetTaggedObject()); + list->DumpForSnapshot(vec); + vec.push_back(std::make_pair(CString("NextIndex"), JSTaggedValue(GetNextIndex()))); + JSObject::DumpForSnapshot(vec); +} + +void JSAPILinkedList::Dump(std::ostream &os) const +{ + TaggedDoubleList *linkedList = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + os << " - length: " << std::dec << linkedList->GetCapacityFromTaggedArray() << "\n"; + os << " - node num: " << std::dec << linkedList->NumberOfNodes() << "\n"; + os << " - delete node num: " << std::dec << linkedList->NumberOfDeletedNodes() << "\n"; + JSObject::Dump(os); + linkedList->Dump(os); +} + +void JSAPILinkedList::DumpForSnapshot(std::vector> &vec) const +{ + TaggedDoubleList *map = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + map->DumpForSnapshot(vec); + + JSObject::DumpForSnapshot(vec); +} + +void JSAPILinkedListIterator::Dump(std::ostream &os) const +{ + TaggedDoubleList *linkedList = TaggedDoubleList::Cast(GetIteratedLinkedList().GetTaggedObject()); + os << " - length: " << std::dec << linkedList->GetCapacityFromTaggedArray() << "\n"; + os << " - node num: " << std::dec << linkedList->NumberOfNodes() << "\n"; + os << " - delete node num: " << std::dec << linkedList->NumberOfDeletedNodes() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex() << "\n"; + JSObject::Dump(os); + linkedList->Dump(os); +} + +void JSAPILinkedListIterator::DumpForSnapshot(std::vector> &vec) const +{ + TaggedDoubleList *linkedList = TaggedDoubleList::Cast(GetIteratedLinkedList().GetTaggedObject()); + linkedList->DumpForSnapshot(vec); + vec.push_back(std::make_pair(CString("NextIndex"), JSTaggedValue(GetNextIndex()))); + JSObject::DumpForSnapshot(vec); +} + void JSAPIQueue::Dump(std::ostream &os) const { os << " - length: " << std::dec << GetSize() << "\n"; @@ -3082,6 +3233,18 @@ static void DumpObject(TaggedObject *obj, case JSType::JS_API_STACK_ITERATOR: JSAPIStackIterator::Cast(obj)->DumpForSnapshot(vec); return; + case JSType::JS_API_LIST: + JSAPIList::Cast(obj)->DumpForSnapshot(vec); + return; + case JSType::JS_API_LINKED_LIST: + JSAPILinkedList::Cast(obj)->DumpForSnapshot(vec); + return; + case JSType::JS_API_LIST_ITERATOR: + JSAPIListIterator::Cast(obj)->DumpForSnapshot(vec); + return; + case JSType::JS_API_LINKED_LIST_ITERATOR: + JSAPILinkedListIterator::Cast(obj)->DumpForSnapshot(vec); + return; case JSType::SOURCE_TEXT_MODULE_RECORD: SourceTextModule::Cast(obj)->DumpForSnapshot(vec); return; @@ -3312,6 +3475,30 @@ void TaggedTreeSet::DumpForSnapshot(std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfNodes(); + for (int index = 0; index < capacity; index++) { + JSTaggedValue val = GetElement(index); + CString str; + KeyToStd(str, JSTaggedValue(index)); + vec.push_back(std::make_pair(str, val)); + } +} + +void TaggedSingleList::DumpForSnapshot(std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfNodes(); + for (int index = 0; index < capacity; index++) { + JSTaggedValue val = GetElement(index); + CString str; + KeyToStd(str, JSTaggedValue(index)); + vec.push_back(std::make_pair(str, val)); + } +} + void JSObject::DumpForSnapshot(std::vector> &vec) const { DISALLOW_GARBAGE_COLLECTION; @@ -3769,6 +3956,9 @@ void GlobalEnv::DumpForSnapshot(std::vector> & std::make_pair(CString("PlainArrayIteratorPrototype"), globalConst->GetPlainArrayIteratorPrototype())); vec.push_back(std::make_pair(CString("DequeIteratorPrototype"), globalConst->GetDequeIteratorPrototype())); vec.push_back(std::make_pair(CString("StackIteratorPrototype"), globalConst->GetStackIteratorPrototype())); + vec.push_back(std::make_pair(CString( + "LinkedListIteratorPrototype"), globalConst->GetLinkedListIteratorPrototype())); + vec.push_back(std::make_pair(CString("ListIteratorPrototype"), globalConst->GetListIteratorPrototype())); } void JSDataView::DumpForSnapshot(std::vector> &vec) const diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index 550339f4..34809cbe 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -29,6 +29,8 @@ #include "ecmascript/jobs/pending_job.h" #include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_api_list_iterator.h" #include "ecmascript/js_api_plain_array_iterator.h" #include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_stack_iterator.h" @@ -190,6 +192,11 @@ void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHCl 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_LINKED_LIST_ITERATOR_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSAPILinkedListIterator::SIZE, JSType::JS_API_LINKED_LIST_ITERATOR)); + SetConstant(ConstantIndex::JS_API_LIST_ITERATOR_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSAPIListIterator::SIZE, JSType::JS_API_LIST_ITERATOR)); SetConstant( ConstantIndex::JS_API_PLAIN_ARRAY_ITERATOR_CLASS_INDEX, factory->NewEcmaDynClass(dynClassClass, JSAPIPlainArrayIterator::SIZE, JSType::JS_API_PLAIN_ARRAY_ITERATOR)); @@ -246,6 +253,10 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) SetConstant(ConstantIndex::STACK_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); SetConstant(ConstantIndex::VECTOR_FUNCTION_INDEX, JSTaggedValue::Undefined()); SetConstant(ConstantIndex::VECTOR_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::LIST_FUNCTION_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::LINKED_LIST_FUNCTION_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::LIST_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::LINKED_LIST_ITERATOR_PROTOTYPE_INDEX, JSTaggedValue::Undefined()); /* SymbolTable *RegisterSymbols */ SetConstant(ConstantIndex::NAME_STRING_INDEX, factory->NewFromASCIINonMovable("name")); SetConstant(ConstantIndex::GETPROTOTYPEOF_STRING_INDEX, factory->NewFromASCIINonMovable("getPrototypeOf")); diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 933a6d50..44d0e77f 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -85,6 +85,8 @@ class JSThread; 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, JSAPIPlainArrayIteratorClass, JS_API_PLAIN_ARRAY_ITERATOR_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSAPILinkedListIteratorClass, JS_API_LINKED_LIST_ITERATOR_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSAPIListIteratorClass, JS_API_LIST_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, JSAPIVectorIteratorClass, JS_API_VECTOR_ITERATOR_CLASS_INDEX, ecma_roots_class) \ @@ -125,6 +127,10 @@ class JSThread; V(JSTaggedValue, PlainArrayFunction, PLAIN_ARRAY_FUNCTION_INDEX, PlainArrayFunction) \ V(JSTaggedValue, DequeIteratorPrototype, DEQUE_ITERATOR_PROTOTYPE_INDEX, DequeIterator) \ V(JSTaggedValue, StackIteratorPrototype, STACK_ITERATOR_PROTOTYPE_INDEX, StackIterator) \ + V(JSTaggedValue, ListFunction, LIST_FUNCTION_INDEX, ListFunction) \ + V(JSTaggedValue, LinkedListFunction, LINKED_LIST_FUNCTION_INDEX, LinkedListFunction) \ + V(JSTaggedValue, ListIteratorPrototype, LIST_ITERATOR_PROTOTYPE_INDEX, ListIterator) \ + V(JSTaggedValue, LinkedListIteratorPrototype, LINKED_LIST_ITERATOR_PROTOTYPE_INDEX, LinkedListIterator) \ /* SymbolTable*RegisterSymbols */ \ V(JSTaggedValue, NameString, NAME_STRING_INDEX, name) \ V(JSTaggedValue, GetPrototypeOfString, GETPROTOTYPEOF_STRING_INDEX, getPrototypeOf) \ diff --git a/ecmascript/ic/ic_runtime.cpp b/ecmascript/ic/ic_runtime.cpp index 92a435ee..85f1921a 100644 --- a/ecmascript/ic/ic_runtime.cpp +++ b/ecmascript/ic/ic_runtime.cpp @@ -135,7 +135,7 @@ void ICRuntime::TraceIC([[maybe_unused]] JSHandle receiver, JSTaggedValue LoadICRuntime::LoadMiss(JSHandle receiver, JSHandle key) { - if (receiver->IsTypedArray() || !receiver->IsJSObject()) { + if (receiver->IsTypedArray() || !receiver->IsJSObject() || receiver->IsSpecialContainer()) { return JSTaggedValue::GetProperty(thread_, receiver, key).GetValue().GetTaggedValue(); } diff --git a/ecmascript/interpreter/fast_runtime_stub-inl.h b/ecmascript/interpreter/fast_runtime_stub-inl.h index 62f347e8..ccf68f35 100644 --- a/ecmascript/interpreter/fast_runtime_stub-inl.h +++ b/ecmascript/interpreter/fast_runtime_stub-inl.h @@ -23,6 +23,8 @@ #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/js_api_arraylist.h" #include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_list.h" #include "ecmascript/js_api_plain_array.h" #include "ecmascript/js_api_queue.h" #include "ecmascript/js_api_stack.h" @@ -1411,6 +1413,14 @@ JSTaggedValue FastRuntimeStub::GetContainerProperty(JSThread *thread, JSTaggedVa res = JSAPIVector::Get(thread, JSHandle::Cast(self), index); break; } + case JSType::JS_API_LIST: { + res = JSAPIList::Cast(receiver.GetTaggedObject())->Get(index); + break; + } + case JSType::JS_API_LINKED_LIST: { + res = JSAPILinkedList::Cast(receiver.GetTaggedObject())->Get(index); + break; + } default: break; } @@ -1428,9 +1438,11 @@ 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_PLAIN_ARRAY: - res = JSAPIPlainArray::Set(thread, JSHandle (thread, receiver), index, value); + case JSType::JS_API_PLAIN_ARRAY: { + JSHandle plainArray(thread, receiver); + res = JSAPIPlainArray::Set(thread, plainArray, index, value); break; + } case JSType::JS_API_DEQUE: res = JSAPIDeque::Cast(receiver.GetTaggedObject())->Set(thread, index, value); break; @@ -1440,6 +1452,16 @@ JSTaggedValue FastRuntimeStub::SetContainerProperty(JSThread *thread, JSTaggedVa case JSType::JS_API_VECTOR: res = JSAPIVector::Cast(receiver.GetTaggedObject())->Set(thread, index, value); break; + case JSType::JS_API_LIST: { + JSHandle singleList(thread, receiver); + res = JSAPIList::Set(thread, singleList, index, JSHandle(thread, value)); + break; + } + case JSType::JS_API_LINKED_LIST: { + JSHandle doubleList(thread, receiver); + res = JSAPILinkedList::Set(thread, doubleList, index, JSHandle(thread, value)); + break; + } default: break; } diff --git a/ecmascript/js_api_arraylist.cpp b/ecmascript/js_api_arraylist.cpp index b21f0e7e..f50d2ed1 100644 --- a/ecmascript/js_api_arraylist.cpp +++ b/ecmascript/js_api_arraylist.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 @@ -117,7 +117,7 @@ JSTaggedValue JSAPIArrayList::Get(JSThread *thread, const uint32_t index) bool JSAPIArrayList::IsEmpty(const JSHandle &arrayList) { - return arrayList->GetLength().GetArrayLength() == 0; + return arrayList->GetSize() == 0; } int JSAPIArrayList::GetIndexOf(JSThread *thread, const JSHandle &arrayList, @@ -226,14 +226,14 @@ JSTaggedValue JSAPIArrayList::ReplaceAllElements(JSThread *thread, const JSHandl const JSHandle &callbackFn, const JSHandle &thisArg) { - JSHandle arraylist = JSHandle::Cast(thisHandle); - uint32_t length = static_cast(arraylist->GetSize()); + JSHandle arrayList = JSHandle::Cast(thisHandle); + uint32_t length = static_cast(arrayList->GetSize()); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); const size_t argsLength = 3; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); for (uint32_t k = 0; k < length; k++) { - kValue.Update(arraylist->Get(thread, k)); + kValue.Update(arrayList->Get(thread, k)); key.Update(JSTaggedValue(k)); EcmaRuntimeCallInfo info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, argsLength); @@ -241,7 +241,7 @@ JSTaggedValue JSAPIArrayList::ReplaceAllElements(JSThread *thread, const JSHandl JSTaggedValue funcResult = JSFunction::Call(&info); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); - arraylist->Set(thread, k, funcResult); + arrayList->Set(thread, k, funcResult); } return JSTaggedValue::Undefined(); @@ -338,7 +338,7 @@ JSHandle JSAPIArrayList::GrowCapacity(const JSThread *thread, const return newElements; } -bool JSAPIArrayList::Has(JSTaggedValue value) const +bool JSAPIArrayList::Has(const JSTaggedValue value) const { TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); int32_t length = GetSize(); @@ -368,10 +368,10 @@ JSHandle JSAPIArrayList::OwnKeys(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc) + const JSHandle &key) { uint32_t index = 0; - if (!UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); } @@ -379,7 +379,10 @@ bool JSAPIArrayList::GetOwnProperty(JSThread *thread, const JSHandle= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); } - return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + + obj->Get(thread, index); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; } JSTaggedValue JSAPIArrayList::GetIteratorObj(JSThread *thread, const JSHandle &obj) @@ -389,4 +392,17 @@ JSTaggedValue JSAPIArrayList::GetIteratorObj(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + int length = obj->GetLength().GetInt(); + int index = key->GetInt(); + if (index < 0 || index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + return OperationResult(thread, obj->Get(thread, index), PropertyMetaData(false)); +} } // namespace panda::ecmascript diff --git a/ecmascript/js_api_arraylist.h b/ecmascript/js_api_arraylist.h index 41ab8b17..25610621 100644 --- a/ecmascript/js_api_arraylist.h +++ b/ecmascript/js_api_arraylist.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 @@ -67,13 +67,15 @@ public: JSTaggedValue Set(JSThread *thread, const uint32_t index, JSTaggedValue value); JSTaggedValue Get(JSThread *thread, const uint32_t index); - bool Has(JSTaggedValue value) const; + bool Has(const JSTaggedValue value) const; static JSHandle OwnKeys(JSThread *thread, const JSHandle &obj); static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc); + const JSHandle &key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); inline int GetSize() const { - return GetLength().GetArrayLength(); + return GetLength().GetInt(); } static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; diff --git a/ecmascript/js_api_arraylist_iterator.cpp b/ecmascript/js_api_arraylist_iterator.cpp index 70f9a823..e74bbd06 100644 --- a/ecmascript/js_api_arraylist_iterator.cpp +++ b/ecmascript/js_api_arraylist_iterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_arraylist_iterator.h b/ecmascript/js_api_arraylist_iterator.h index 6067b5af..377ff00c 100644 --- a/ecmascript/js_api_arraylist_iterator.h +++ b/ecmascript/js_api_arraylist_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_deque.cpp b/ecmascript/js_api_deque.cpp index c8ac13db..c7eb35c6 100644 --- a/ecmascript/js_api_deque.cpp +++ b/ecmascript/js_api_deque.cpp @@ -161,6 +161,7 @@ JSTaggedValue JSAPIDeque::Get(const uint32_t index) JSTaggedValue JSAPIDeque::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) { + ASSERT(index < GetSize()); TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); uint32_t capacity = elements->GetLength(); uint32_t first = GetFirst(); @@ -214,10 +215,10 @@ JSHandle JSAPIDeque::OwnKeys(JSThread *thread, const JSHandle &deque, - const JSHandle &key, PropertyDescriptor &desc) + const JSHandle &key) { uint32_t index = 0; - if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); } @@ -225,7 +226,9 @@ bool JSAPIDeque::GetOwnProperty(JSThread *thread, const JSHandle &de if (index >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); } - return JSObject::GetOwnProperty(thread, JSHandle::Cast(deque), key, desc); + + deque->Get(index); + return true; } JSTaggedValue JSAPIDeque::GetIteratorObj(JSThread *thread, const JSHandle &deque) @@ -235,4 +238,17 @@ JSTaggedValue JSAPIDeque::GetIteratorObj(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + int length = static_cast(obj->GetSize()); + int index = key->GetInt(); + if (index < 0 || index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + return OperationResult(thread, obj->Get(index), PropertyMetaData(false)); +} } // namespace panda::ecmascript diff --git a/ecmascript/js_api_deque.h b/ecmascript/js_api_deque.h index afce0c6a..ed56b1be 100644 --- a/ecmascript/js_api_deque.h +++ b/ecmascript/js_api_deque.h @@ -39,9 +39,9 @@ public: static JSHandle OwnKeys(JSThread *thread, const JSHandle &deque); - static bool GetOwnProperty(JSThread *thread, const JSHandle &deque, const JSHandle &key, - PropertyDescriptor &desc); - + static bool GetOwnProperty(JSThread *thread, const JSHandle &deque, const JSHandle &key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); JSTaggedValue GetFront(); JSTaggedValue GetTail(); diff --git a/ecmascript/js_api_deque_iterator.cpp b/ecmascript/js_api_deque_iterator.cpp index f4905b22..ab77f917 100644 --- a/ecmascript/js_api_deque_iterator.cpp +++ b/ecmascript/js_api_deque_iterator.cpp @@ -1,4 +1,3 @@ - /* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,23 +35,26 @@ JSTaggedValue JSAPIDequeIterator::Next(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an deque iterator", JSTaggedValue::Exception()); } JSHandle iter(input); - JSHandle deque(thread, iter->GetIteratedDeque()); + JSHandle iteratorDeque(thread, iter->GetIteratedDeque()); JSHandle undefinedHandle = thread->GlobalConstants()->GetHandledUndefined(); - if (deque->IsUndefined()) { + if (iteratorDeque->IsUndefined()) { return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); } + JSHandle deque = JSHandle::Cast(iteratorDeque); uint32_t index = iter->GetNextIndex(); - JSHandle elements(thread, JSHandle(deque)->GetElements()); + JSHandle elements(thread, deque->GetElements()); array_size_t capacity = elements->GetLength(); - uint32_t last = JSHandle(deque)->GetLast(); + uint32_t first = deque->GetFirst(); + uint32_t last = 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(); + uint32_t elementIndex = (index + capacity - first) % capacity; + JSHandle value = JSTaggedValue::GetProperty(thread, iteratorDeque, elementIndex).GetValue(); return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); } diff --git a/ecmascript/js_api_linked_list.cpp b/ecmascript/js_api_linked_list.cpp new file mode 100644 index 00000000..7b62f576 --- /dev/null +++ b/ecmascript/js_api_linked_list.cpp @@ -0,0 +1,234 @@ +/* + * 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_linked_list.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "tagged_list.h" + +namespace panda::ecmascript { +JSTaggedValue JSAPILinkedList::Insert(JSThread *thread, const JSHandle &list, + const JSHandle &value, const int index) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + if (index < 0 || index > nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", + JSTaggedValue::Exception()); + } + JSTaggedValue newList = TaggedDoubleList::Insert(thread, doubleList, value, index); + list->SetDoubleList(thread, newList); + return JSTaggedValue::True(); +} + +void JSAPILinkedList::Clear(JSThread *thread) +{ + TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + doubleList->Clear(thread); +} + +JSHandle JSAPILinkedList::Clone(JSThread *thread, const JSHandle &list) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int capacity = doubleList->NumberOfNodes(); + JSHandle newLinkedList = thread->GetEcmaVM()->GetFactory()->NewJSAPILinkedList(); + JSTaggedValue newTaggedList = TaggedDoubleList::Create(thread, capacity); + JSHandle newDoubleList(thread, newTaggedList); + doubleList->CopyArray(thread, newDoubleList); + newLinkedList->SetDoubleList(thread, newDoubleList.GetTaggedValue()); + return newLinkedList; +} + +JSTaggedValue JSAPILinkedList::RemoveFirst(JSThread *thread, const JSHandle &list) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + if (nodeLength < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "there is no element", JSTaggedValue::Exception()); + } + return doubleList->RemoveFirst(thread); +} + +JSTaggedValue JSAPILinkedList::RemoveLast(JSThread *thread, const JSHandle &list) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + if (nodeLength < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "there is no element", JSTaggedValue::Exception()); + } + return doubleList->RemoveLast(thread); +} + +JSTaggedValue JSAPILinkedList::RemoveByIndex(JSThread *thread, JSHandle &list, const int index) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + if (index < 0 || index >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); + } + return doubleList->RemoveByIndex(thread, index); +} + +JSTaggedValue JSAPILinkedList::Remove(JSThread *thread, const JSTaggedValue &element) +{ + TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + int nodeLength = doubleList->Length(); + if (nodeLength < 0) { + return JSTaggedValue::False(); + } + return doubleList->Remove(thread, element); +} + +JSTaggedValue JSAPILinkedList::RemoveFirstFound(JSThread *thread, JSHandle &list, + const JSTaggedValue &element) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + if (nodeLength < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "there is no element", JSTaggedValue::Exception()); + } + return TaggedDoubleList::RemoveFirstFound(thread, doubleList, element); +} + +JSTaggedValue JSAPILinkedList::RemoveLastFound(JSThread *thread, JSHandle &list, + const JSTaggedValue &element) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + if (nodeLength < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "there is no element", JSTaggedValue::Exception()); + } + return TaggedDoubleList::RemoveLastFound(thread, doubleList, element); +} + +void JSAPILinkedList::Add(JSThread *thread, const JSHandle &list, const JSHandle &value) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + JSTaggedValue newLinkedList = TaggedDoubleList::Add(thread, doubleList, value); + list->SetDoubleList(thread, newLinkedList); +} + +void JSAPILinkedList::AddFirst(JSThread *thread, const JSHandle &list, + const JSHandle &value) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + JSTaggedValue newLinkedList = TaggedDoubleList::AddFirst(thread, doubleList, value); + list->SetDoubleList(thread, newLinkedList); +} + +JSTaggedValue JSAPILinkedList::GetFirst() +{ + JSTaggedValue res = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject())->GetFirst(); + if (res.IsHole()) { + return JSTaggedValue::Undefined(); + } + return res; +} + +JSTaggedValue JSAPILinkedList::GetLast() +{ + JSTaggedValue res = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject())->GetLast(); + if (res.IsHole()) { + return JSTaggedValue::Undefined(); + } + return res; +} + +JSTaggedValue JSAPILinkedList::Set(JSThread *thread, const JSHandle &list, + const int index, const JSHandle &value) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + if (index < 0 || index >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); + } + JSTaggedValue oldValue = doubleList->Get(index); + TaggedDoubleList::Set(thread, doubleList, index, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return oldValue; +} + +bool JSAPILinkedList::Has(const JSTaggedValue &element) +{ + TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + return doubleList->Has(element); +} + +JSTaggedValue JSAPILinkedList::Get(const int index) +{ + TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + int nodeLength = doubleList->Length(); + if (index < 0 || index >= nodeLength) { + return JSTaggedValue::Undefined(); + } + return doubleList->Get(index); +} + +JSTaggedValue JSAPILinkedList::GetIndexOf(const JSTaggedValue &element) +{ + TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + return JSTaggedValue(doubleList->GetIndexOf(element)); +} + +JSTaggedValue JSAPILinkedList::GetLastIndexOf(const JSTaggedValue &element) +{ + TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + return JSTaggedValue(doubleList->GetLastIndexOf(element)); +} + +JSTaggedValue JSAPILinkedList::ConvertToArray(const JSThread *thread, const JSHandle &list) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + return TaggedDoubleList::ConvertToArray(thread, doubleList); +} + +JSHandle JSAPILinkedList::OwnKeys(JSThread *thread, const JSHandle &list) +{ + JSHandle doubleList(thread, list->GetDoubleList().GetTaggedObject()); + return TaggedDoubleList::OwnKeys(thread, doubleList); +} + +bool JSAPILinkedList::GetOwnProperty(JSThread *thread, const JSHandle &list, + const JSHandle &key) +{ + 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); + } + JSHandle doubleList(thread, list->GetDoubleList()); + uint32_t length = static_cast(doubleList->Length()); + if (index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index out-of-bounds", false); + } + + list->Get(index); + return true; +} + +OperationResult JSAPILinkedList::GetProperty(JSThread *thread, const JSHandle &list, + const JSHandle &key) +{ + JSHandle doubleList(thread, list->GetDoubleList()); + int nodeLength = doubleList->Length(); + int index = key->GetInt(); + if (index < 0 || index >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index out-of-bounds", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + return OperationResult(thread, doubleList->Get(index), PropertyMetaData(false)); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_linked_list.h b/ecmascript/js_api_linked_list.h new file mode 100644 index 00000000..c317f5f1 --- /dev/null +++ b/ecmascript/js_api_linked_list.h @@ -0,0 +1,72 @@ +/* + * 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_JSAPILINKEDLIST_H +#define ECMASCRIPT_JSAPILINKEDLIST_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" +#include "tagged_list.h" + +namespace panda::ecmascript { +class JSAPILinkedList : public JSObject { +public: + static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static JSAPILinkedList *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSAPILinkedList()); + return static_cast(object); + } + + static void Add(JSThread *thread, const JSHandle &list, const JSHandle &value); + static JSHandle Clone(JSThread *thread, const JSHandle &list); + static void AddFirst(JSThread *thread, const JSHandle &list, const JSHandle &value); + static JSTaggedValue Insert(JSThread *thread, const JSHandle &list, + const JSHandle &value, const int index); + static JSTaggedValue Set(JSThread *thread, const JSHandle &list, + const int index, const JSHandle &value); + static JSTaggedValue ConvertToArray(const JSThread *thread, const JSHandle &list); + static JSHandle OwnKeys(JSThread *thread, const JSHandle &list); + static bool GetOwnProperty(JSThread *thread, const JSHandle &list, + const JSHandle &key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &list, + const JSHandle &key); + static JSTaggedValue RemoveFirst(JSThread *thread, const JSHandle &list); + static JSTaggedValue RemoveLast(JSThread *thread, const JSHandle &list); + static JSTaggedValue RemoveByIndex(JSThread *thread, JSHandle &list, const int index); + static JSTaggedValue RemoveFirstFound(JSThread *thread, JSHandle &list, + const JSTaggedValue &element); + static JSTaggedValue RemoveLastFound(JSThread *thread, JSHandle &list, + const JSTaggedValue &element); + void Clear(JSThread *thread); + bool Has(const JSTaggedValue &element); + JSTaggedValue GetFirst(); + JSTaggedValue GetLast(); + JSTaggedValue Get(const int index); + JSTaggedValue Remove(JSThread *thread, const JSTaggedValue &element); + JSTaggedValue GetIndexOf(const JSTaggedValue &element); + JSTaggedValue GetLastIndexOf(const JSTaggedValue &element); + inline int Length() + { + return TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject())->Length(); + } + static constexpr size_t DOUBLE_LIST_OFFSET = JSObject::SIZE; + ACCESSORS(DoubleList, DOUBLE_LIST_OFFSET, SIZE); + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, DOUBLE_LIST_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSAPILinkedLIST_H diff --git a/ecmascript/js_api_linked_list_iterator.cpp b/ecmascript/js_api_linked_list_iterator.cpp new file mode 100644 index 00000000..0579a108 --- /dev/null +++ b/ecmascript/js_api_linked_list_iterator.cpp @@ -0,0 +1,66 @@ +/* + * 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_linked_list_iterator.h" +#include "builtins/builtins_errors.h" +#include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "global_env.h" +#include "js_api_linked_list.h" +#include "js_array.h" +#include "object_factory.h" +#include "tagged_list.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSAPILinkedListIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle input(BuiltinsBase::GetThis(argv)); + if (!input->IsJSAPILinkedListIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an linkedList iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + JSHandle linkedList(thread, iter->GetIteratedLinkedList()); + JSHandle undefinedHandle = thread->GlobalConstants()->GetHandledUndefined(); + JSHandle list(linkedList); + if (linkedList->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + int index = iter->GetNextIndex(); + int length = list->Length(); + if (index >= length) { + iter->SetIteratedLinkedList(thread, undefinedHandle); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + iter->SetNextIndex(index + 1); + JSHandle value(thread, list->Get(index)); + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); +} + +JSHandle JSAPILinkedListIterator::CreateLinkedListIterator(JSThread *thread, + const JSHandle &obj) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSAPILinkedList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPILinkedList", + thread->GlobalConstants()->GetHandledUndefined()); + } + JSHandle iter(factory->NewJSAPILinkedListIterator(JSHandle(obj))); + return iter; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_linked_list_iterator.h b/ecmascript/js_api_linked_list_iterator.h new file mode 100644 index 00000000..ae3fddd1 --- /dev/null +++ b/ecmascript/js_api_linked_list_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_LINKED_LIST_ITERATOR_H +#define ECMASCRIPT_JS_API_LINKED_LIST_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSAPILinkedListIterator : public JSObject { +public: + static JSAPILinkedListIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSAPILinkedListIterator()); + return static_cast(obj); + } + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + static JSHandle CreateLinkedListIterator(JSThread *thread, const JSHandle &obj); + static constexpr size_t ITERATED_LINKED_LIST_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedLinkedList, ITERATED_LINKED_LIST_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_LINKED_LIST_OFFSET, NEXT_INDEX_OFFSET) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_LinkedList_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api_list.cpp b/ecmascript/js_api_list.cpp new file mode 100644 index 00000000..811a8542 --- /dev/null +++ b/ecmascript/js_api_list.cpp @@ -0,0 +1,218 @@ +/* + * 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_list.h" +#include "ecmascript/js_api_list_iterator.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_list.h" + +namespace panda::ecmascript { +void JSAPIList::Add(JSThread *thread, const JSHandle &list, const JSHandle &value) +{ + JSHandle singleList(thread, list->GetSingleList()); + JSTaggedValue newList = TaggedSingleList::Add(thread, singleList, value); + list->SetSingleList(thread, newList); +} + +JSTaggedValue JSAPIList::GetFirst() +{ + JSTaggedValue res = TaggedSingleList::Cast(GetSingleList().GetTaggedObject())->GetFirst(); + if (res.IsHole()) { + return JSTaggedValue::Undefined(); + } + return res; +} + +JSTaggedValue JSAPIList::GetLast() +{ + JSTaggedValue res = TaggedSingleList::Cast(GetSingleList().GetTaggedObject())->GetLast(); + if (res.IsHole()) { + return JSTaggedValue::Undefined(); + } + return res; +} + +JSTaggedValue JSAPIList::Insert(JSThread *thread, const JSHandle &list, const JSHandle &value, + const int index) +{ + JSHandle singleList(thread, list->GetSingleList()); + int nodeLength = singleList->Length(); + if (index < 0 || index > nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); + } + JSTaggedValue newList = TaggedSingleList::Insert(thread, singleList, value, index); + list->SetSingleList(thread, newList); + return JSTaggedValue::True(); +} + +JSTaggedValue JSAPIList::Set(JSThread *thread, const JSHandle &list, + const int index, const JSHandle &value) +{ + JSHandle singleList(thread, list->GetSingleList()); + int nodeLength = singleList->Length(); + if (index < 0 || index >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); + } + JSTaggedValue oldValue = singleList->Get(index); + TaggedSingleList::Set(thread, singleList, index, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return oldValue; +} + +bool JSAPIList::Has(const JSTaggedValue &element) +{ + TaggedSingleList *singleList = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + return singleList->Has(element); +} + +bool JSAPIList::IsEmpty() +{ + return TaggedSingleList::Cast(GetSingleList().GetTaggedObject())->IsEmpty(); +} + +JSTaggedValue JSAPIList::Get(const int index) +{ + TaggedSingleList *singleList = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + int nodeLength = singleList->Length(); + if (index < 0 || index >= nodeLength) { + return JSTaggedValue::Undefined(); + } + return singleList->Get(index); +} + +JSTaggedValue JSAPIList::GetIndexOf(const JSTaggedValue &element) +{ + TaggedSingleList *singleList = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + return JSTaggedValue(singleList->GetIndexOf(element)); +} + +JSTaggedValue JSAPIList::GetLastIndexOf(const JSTaggedValue &element) +{ + TaggedSingleList *singleList = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + return JSTaggedValue(singleList->GetLastIndexOf(element)); +} + +void JSAPIList::Clear(JSThread *thread) +{ + TaggedSingleList *singleList = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + singleList->Clear(thread); +} + +JSTaggedValue JSAPIList::RemoveByIndex(JSThread *thread, const JSHandle &list, const int &index) +{ + JSHandle singleList(thread, list->GetSingleList()); + int nodeLength = singleList->Length(); + if (index < 0 || index >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); + } + return singleList->RemoveByIndex(thread, index); +} + +JSTaggedValue JSAPIList::Remove(JSThread *thread, const JSTaggedValue &element) +{ + TaggedSingleList *singleList = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + return singleList->Remove(thread, element); +} + +JSTaggedValue JSAPIList::ReplaceAllElements(JSThread *thread, const JSHandle &thisHandle, + const JSHandle &callbackFn, + const JSHandle &thisArg) +{ + JSHandle list = JSHandle::Cast(thisHandle); + JSHandle singleList(thread, list->GetSingleList()); + return TaggedSingleList::ReplaceAllElements(thread, thisHandle, callbackFn, thisArg, singleList); +} + +JSTaggedValue JSAPIList::Sort(JSThread *thread, const JSHandle &thisHandle, + const JSHandle &callbackFn) +{ + JSHandle list = JSHandle::Cast(thisHandle); + JSHandle singleList(thread, list->GetSingleList()); + return TaggedSingleList::Sort(thread, callbackFn, singleList); +} + +JSTaggedValue JSAPIList::Equal(JSThread *thread, const JSHandle &list) +{ + JSHandle compareList(thread, list->GetSingleList()); + return TaggedSingleList::Cast(GetSingleList().GetTaggedObject())->Equal(compareList); +} + +JSTaggedValue JSAPIList::ConvertToArray(const JSThread *thread, const JSHandle &list) +{ + JSHandle singleList(thread, list->GetSingleList()); + return TaggedSingleList::ConvertToArray(thread, singleList); +} + +JSTaggedValue JSAPIList::GetSubList(JSThread *thread, const JSHandle &list, + const int fromIndex, const int toIndex) +{ + JSHandle singleList(thread, list->GetSingleList()); + int nodeLength = singleList->Length(); + if (fromIndex < 0 || toIndex < 0 || toIndex >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex or the toIndex is out-of-bounds", + JSTaggedValue::Exception()); + } + if (fromIndex >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "the toIndex cannot be less than or equal to fromIndex", + JSTaggedValue::Exception()); + } + int len = TaggedSingleList::ELEMENTS_START_INDEX + (toIndex - fromIndex) * TaggedSingleList::ENTRY_SIZE; + JSHandle sublist = thread->GetEcmaVM()->GetFactory()->NewJSAPIList(); + JSHandle subSingleList(thread, TaggedSingleList::Create(thread, len)); + TaggedSingleList::GetSubList(thread, singleList, fromIndex, toIndex, subSingleList); + sublist->SetSingleList(thread, subSingleList); + return sublist.GetTaggedValue(); +} + +JSHandle JSAPIList::OwnKeys(JSThread *thread, const JSHandle &list) +{ + JSHandle singleList(thread, list->GetSingleList()); + return TaggedSingleList::OwnKeys(thread, singleList); +} + +bool JSAPIList::GetOwnProperty(JSThread *thread, const JSHandle &list, const JSHandle &key) +{ + 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); + } + JSHandle singleList(thread, list->GetSingleList()); + uint32_t length = static_cast(singleList->Length()); + if (index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); + } + list->Get(index); + return true; +} + +OperationResult JSAPIList::GetProperty(JSThread *thread, const JSHandle &list, + const JSHandle &key) +{ + JSHandle singleList(thread, list->GetSingleList()); + int nodeLength = singleList->Length(); + int index = key->GetInt(); + if (index < 0 || index >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + return OperationResult(thread, singleList->Get(index), PropertyMetaData(false)); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_list.h b/ecmascript/js_api_list.h new file mode 100644 index 00000000..6655f17e --- /dev/null +++ b/ecmascript/js_api_list.h @@ -0,0 +1,75 @@ +/* + * 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_JSAPILIST_H +#define ECMASCRIPT_JSAPILIST_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" +#include "tagged_list.h" + +namespace panda::ecmascript { +class JSAPIList : public JSObject { +public: + static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static JSAPIList *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSAPIList()); + return static_cast(object); + } + + static void Add(JSThread *thread, const JSHandle &list, const JSHandle &value); + static JSTaggedValue Insert(JSThread *thread, const JSHandle &list, + const JSHandle &value, const int index); + static JSTaggedValue Set(JSThread *thread, const JSHandle &list, + const int index, const JSHandle &value); + static JSTaggedValue ReplaceAllElements(JSThread *thread, const JSHandle &thisHandle, + const JSHandle &callbackFn, + const JSHandle &thisArg); + static JSTaggedValue Sort(JSThread *thread, const JSHandle &thisHandle, + const JSHandle &callbackFn); + static JSTaggedValue ConvertToArray(const JSThread *thread, const JSHandle &list); + static JSTaggedValue GetSubList(JSThread *thread, const JSHandle &list, + const int fromIndex, const int toIndex); + static JSTaggedValue RemoveByIndex(JSThread *thread, const JSHandle &list, const int &index); + static JSHandle OwnKeys(JSThread *thread, const JSHandle &list); + static bool GetOwnProperty(JSThread *thread, const JSHandle &list, + const JSHandle &key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &list, + const JSHandle &key); + + JSTaggedValue GetFirst(); + JSTaggedValue GetLast(); + bool IsEmpty(); + JSTaggedValue Get(const int index); + bool Has(const JSTaggedValue &element); + JSTaggedValue GetIndexOf(const JSTaggedValue &element); + JSTaggedValue GetLastIndexOf(const JSTaggedValue &element); + JSTaggedValue Equal(JSThread *thread, const JSHandle &list); + void Clear(JSThread *thread); + JSTaggedValue Remove(JSThread *thread, const JSTaggedValue &element); + inline int Length() + { + return TaggedSingleList::Cast(GetSingleList().GetTaggedObject())->Length(); + } + + static constexpr size_t SINGLY_LIST_OFFSET = JSObject::SIZE; + ACCESSORS(SingleList, SINGLY_LIST_OFFSET, SIZE); + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, SINGLY_LIST_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSAPILIST_H diff --git a/ecmascript/js_api_list_iterator.cpp b/ecmascript/js_api_list_iterator.cpp new file mode 100644 index 00000000..f68bd879 --- /dev/null +++ b/ecmascript/js_api_list_iterator.cpp @@ -0,0 +1,63 @@ +/* + * 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_list_iterator.h" +#include "builtins/builtins_errors.h" +#include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "global_env.h" +#include "js_api_list.h" +#include "object_factory.h" +#include "tagged_list.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSAPIListIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle input(BuiltinsBase::GetThis(argv)); + if (!input->IsJSAPIListIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an List iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + JSHandle list(thread, iter->GetIteratedList()); + JSHandle undefinedHandle = thread->GlobalConstants()->GetHandledUndefined(); + JSHandle singleList(list); + if (list->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + int index = iter->GetNextIndex(); + int length = singleList->Length(); + if (index >= length) { + iter->SetIteratedList(thread, undefinedHandle); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + iter->SetNextIndex(index + 1); + JSHandle value(thread, singleList->Get(index)); + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); +} + +JSHandle JSAPIListIterator::CreateListIterator(JSThread *thread, const JSHandle &obj) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSAPIList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIList", thread->GlobalConstants()->GetHandledUndefined()); + } + JSHandle iter(factory->NewJSAPIListIterator(JSHandle(obj))); + return iter; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_list_iterator.h b/ecmascript/js_api_list_iterator.h new file mode 100644 index 00000000..82bbe563 --- /dev/null +++ b/ecmascript/js_api_list_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_LIST_ITERATOR_H +#define ECMASCRIPT_JS_API_LIST_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSAPIListIterator : public JSObject { +public: + static JSAPIListIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSAPIListIterator()); + return static_cast(obj); + } + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + static JSHandle CreateListIterator(JSThread *thread, const JSHandle &obj); + static constexpr size_t ITERATED_LIST_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedList, ITERATED_LIST_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_LIST_OFFSET, NEXT_INDEX_OFFSET) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_List_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api_plain_array.cpp b/ecmascript/js_api_plain_array.cpp index 3a39bc50..765cc66f 100644 --- a/ecmascript/js_api_plain_array.cpp +++ b/ecmascript/js_api_plain_array.cpp @@ -154,15 +154,17 @@ JSTaggedValue JSAPIPlainArray::Set(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc) + const JSHandle &key) { TaggedArray *keyArray = TaggedArray::Cast(obj->GetKeys().GetTaggedObject()); int32_t size = obj->GetLength(); - int32_t index = obj->BinarySearch(keyArray, 0, size, key.GetTaggedValue().GetNumber()); - if (index < 0 || index > size) { + int32_t index = obj->BinarySearch(keyArray, 0, size, key.GetTaggedValue().GetInt()); + if (index < 0 || index >= size) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); } - return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + + obj->Get(key.GetTaggedValue()); + return true; } OperationResult JSAPIPlainArray::GetProperty(JSThread *thread, const JSHandle &obj, @@ -170,8 +172,8 @@ OperationResult JSAPIPlainArray::GetProperty(JSThread *thread, const JSHandleGetKeys().GetTaggedObject()); int32_t size = obj->GetLength(); - int32_t index = obj->BinarySearch(keyArray, 0, size, key.GetTaggedValue().GetNumber()); - if (index < 0 || index > size) { + int32_t index = obj->BinarySearch(keyArray, 0, size, key.GetTaggedValue().GetInt()); + if (index < 0 || index >= size) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); } diff --git a/ecmascript/js_api_plain_array.h b/ecmascript/js_api_plain_array.h index ee0186cc..89f1ee48 100644 --- a/ecmascript/js_api_plain_array.h +++ b/ecmascript/js_api_plain_array.h @@ -31,7 +31,7 @@ public: static void Add(JSThread *thread, const JSHandle &obj, JSHandle key, JSHandle value); static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc); + const JSHandle &key); static JSHandle CreateSlot(const JSThread *thread, const uint32_t capacity); static JSHandle Clone(JSThread *thread, const JSHandle &plainArray); static JSHandle GetIteratorObj(JSThread *thread, const JSHandle &obj, diff --git a/ecmascript/js_api_queue.cpp b/ecmascript/js_api_queue.cpp index 08a4462d..05a4c7de 100644 --- a/ecmascript/js_api_queue.cpp +++ b/ecmascript/js_api_queue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 @@ -14,6 +14,7 @@ */ #include "js_api_queue.h" +#include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/object_factory.h" #include "interpreter/fast_runtime_stub-inl.h" @@ -45,14 +46,13 @@ JSHandle JSAPIQueue::GrowCapacity(const JSThread *thread, const JSH ASSERT(!oldElements->IsDictionaryMode()); uint32_t oldLength = oldElements->GetLength(); uint32_t newCapacity = 0; - // Set the oldLength(DEFAULT_CAPACITY_LENGTH = 8) of elements when constructing ASSERT(oldLength != 0); if (oldLength == 0) { newCapacity = ComputeCapacity(capacity); newElements = thread->GetEcmaVM()->GetFactory()->CopyArray(oldElements, oldLength, newCapacity); } else if ((tail + 1) % oldLength == front) { newCapacity = ComputeCapacity(capacity); - newElements = thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, oldLength, newCapacity, front, tail); + newElements = thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, newCapacity, front, tail); front = 0; tail = oldLength - 1; } else { @@ -101,12 +101,17 @@ JSTaggedValue JSAPIQueue::Pop(JSThread *thread, const JSHandle &queu JSTaggedValue JSAPIQueue::Get(JSThread *thread, const uint32_t index) { - if (index < 0) { + uint32_t length = GetSize(); + if (index >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "Get property index out-of-bounds", JSTaggedValue::Exception()); } TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); - return elements->Get(index); + uint32_t capacity = elements->GetLength(); + uint32_t front = GetCurrentFront(); + ASSERT(capacity != 0); + uint32_t curIndex = (front + index) % capacity; + return elements->Get(curIndex); } JSTaggedValue JSAPIQueue::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) @@ -132,7 +137,6 @@ bool JSAPIQueue::Has(JSTaggedValue value) const if (JSTaggedValue::SameValue(elements->Get(index), value)) { return true; } - // Set the capacity(DEFAULT_CAPACITY_LENGTH = 8) of elements when constructing ASSERT(capacity != 0); index = (index + 1) % capacity; } @@ -153,10 +157,10 @@ JSHandle JSAPIQueue::OwnKeys(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc) + const JSHandle &key) { uint32_t index = 0; - if (!UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); } @@ -164,7 +168,23 @@ bool JSAPIQueue::GetOwnProperty(JSThread *thread, const JSHandle &ob if (index < 0 || index >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); } - return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + + obj->Get(thread, index); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; +} + +OperationResult JSAPIQueue::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + int length = obj->GetLength().GetArrayLength(); + int index = key->GetInt(); + if (index < 0 || index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + return OperationResult(thread, obj->Get(thread, index), PropertyMetaData(false)); } uint32_t JSAPIQueue::GetArrayLength(JSThread *thread, const JSHandle &queue) diff --git a/ecmascript/js_api_queue.h b/ecmascript/js_api_queue.h index 630fb5b0..0df8f3c3 100644 --- a/ecmascript/js_api_queue.h +++ b/ecmascript/js_api_queue.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 @@ -39,8 +39,9 @@ public: bool Has(JSTaggedValue value) const; static JSHandle OwnKeys(JSThread *thread, const JSHandle &obj); - static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - PropertyDescriptor &desc); + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); inline uint32_t GetSize() const { diff --git a/ecmascript/js_api_queue_iterator.cpp b/ecmascript/js_api_queue_iterator.cpp index 184225fc..59d93235 100644 --- a/ecmascript/js_api_queue_iterator.cpp +++ b/ecmascript/js_api_queue_iterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_queue_iterator.h b/ecmascript/js_api_queue_iterator.h index 26832e2a..470dd451 100644 --- a/ecmascript/js_api_queue_iterator.h +++ b/ecmascript/js_api_queue_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_stack.cpp b/ecmascript/js_api_stack.cpp index a70f4a2d..f333fe4a 100644 --- a/ecmascript/js_api_stack.cpp +++ b/ecmascript/js_api_stack.cpp @@ -137,17 +137,32 @@ JSHandle JSAPIStack::OwnKeys(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc) + const JSHandle &key) { uint32_t index = 0; - if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + 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) { + if (index >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); } - return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + + obj->Get(index); + return true; +} + +OperationResult JSAPIStack::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + int length = obj->GetTop() + 1; + int index = key->GetInt(); + if (index < 0 || index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + return OperationResult(thread, obj->Get(index), PropertyMetaData(false)); } } // namespace panda::ecmascript diff --git a/ecmascript/js_api_stack.h b/ecmascript/js_api_stack.h index 3a485b6c..74a92c70 100644 --- a/ecmascript/js_api_stack.h +++ b/ecmascript/js_api_stack.h @@ -34,9 +34,9 @@ public: static JSHandle OwnKeys(JSThread *thread, const JSHandle &obj); - static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - PropertyDescriptor &desc); - + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); bool Empty(); bool Has(JSTaggedValue value) const; diff --git a/ecmascript/js_api_tree_map.cpp b/ecmascript/js_api_tree_map.cpp index a510230b..cac064a4 100644 --- a/ecmascript/js_api_tree_map.cpp +++ b/ecmascript/js_api_tree_map.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_tree_map.h b/ecmascript/js_api_tree_map.h index e996679d..2e948deb 100644 --- a/ecmascript/js_api_tree_map.h +++ b/ecmascript/js_api_tree_map.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_tree_map_iterator.cpp b/ecmascript/js_api_tree_map_iterator.cpp index 9bd5d1d5..d91eb8da 100644 --- a/ecmascript/js_api_tree_map_iterator.cpp +++ b/ecmascript/js_api_tree_map_iterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_tree_map_iterator.h b/ecmascript/js_api_tree_map_iterator.h index e7f5056d..dc950c50 100644 --- a/ecmascript/js_api_tree_map_iterator.h +++ b/ecmascript/js_api_tree_map_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_tree_set.cpp b/ecmascript/js_api_tree_set.cpp index 6b454546..ec43549f 100644 --- a/ecmascript/js_api_tree_set.cpp +++ b/ecmascript/js_api_tree_set.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_tree_set.h b/ecmascript/js_api_tree_set.h index 1b19bc44..83d5e365 100644 --- a/ecmascript/js_api_tree_set.h +++ b/ecmascript/js_api_tree_set.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_tree_set_iterator.cpp b/ecmascript/js_api_tree_set_iterator.cpp index f20ce941..d66a5d03 100644 --- a/ecmascript/js_api_tree_set_iterator.cpp +++ b/ecmascript/js_api_tree_set_iterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_tree_set_iterator.h b/ecmascript/js_api_tree_set_iterator.h index 97915d67..ed25ee7c 100644 --- a/ecmascript/js_api_tree_set_iterator.h +++ b/ecmascript/js_api_tree_set_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/js_api_vector.cpp b/ecmascript/js_api_vector.cpp index 57f63880..327716a6 100644 --- a/ecmascript/js_api_vector.cpp +++ b/ecmascript/js_api_vector.cpp @@ -416,10 +416,10 @@ JSHandle JSAPIVector::OwnKeys(JSThread *thread, const JSHandle &obj, - const JSHandle &key, PropertyDescriptor &desc) + const JSHandle &key) { uint32_t index = 0; - if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); } @@ -427,7 +427,10 @@ bool JSAPIVector::GetOwnProperty(JSThread *thread, const JSHandle & if (index >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); } - return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + + JSAPIVector::Get(thread, obj, index); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; } void JSAPIVector::TrimToCurrentLength(JSThread *thread, const JSHandle &obj) diff --git a/ecmascript/js_api_vector.h b/ecmascript/js_api_vector.h index 41a23eb3..ce19d96f 100644 --- a/ecmascript/js_api_vector.h +++ b/ecmascript/js_api_vector.h @@ -83,8 +83,7 @@ public: bool Has(const JSTaggedValue &value) const; static JSHandle OwnKeys(JSThread *thread, const JSHandle &obj); - static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, - PropertyDescriptor &desc); + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); static void TrimToCurrentLength(JSThread *thread, const JSHandle &vector); diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index c99dec9b..fed3f974 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -106,6 +106,8 @@ class ProtoChangeDetails; JS_API_TREEMAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREESET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_VECTOR_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_LINKED_LIST_ITERATOR, /* ///////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_LIST_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_ARRAY_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_STRING_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_INTL, /* ///////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -130,6 +132,8 @@ class ProtoChangeDetails; JS_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_ARRAY_LIST, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_VECTOR, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_LINKED_LIST, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_LIST, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREE_MAP, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREE_SET, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_DEQUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -718,7 +722,14 @@ public: { return GetObjectType() == JSType::JS_API_QUEUE_ITERATOR; } - + inline bool IsJSAPIList() const + { + return GetObjectType() == JSType::JS_API_LIST; + } + inline bool IsJSAPILinkedList() const + { + return GetObjectType() == JSType::JS_API_LINKED_LIST; + } inline bool IsJSAPITreeMap() const { return GetObjectType() == JSType::JS_API_TREE_MAP; @@ -823,6 +834,16 @@ public: return GetObjectType() == JSType::JS_API_STACK_ITERATOR; } + inline bool IsJSAPILinkedListIterator() const + { + return GetObjectType() == JSType::JS_API_LINKED_LIST_ITERATOR; + } + + inline bool IsJSAPIListIterator() const + { + return GetObjectType() == JSType::JS_API_LIST_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 3eeac9c9..aceba78b 100644 --- a/ecmascript/js_object-inl.h +++ b/ecmascript/js_object-inl.h @@ -196,6 +196,16 @@ inline bool JSObject::IsJSAPIVectorIterator() const return GetJSHClass()->IsJSAPIVectorIterator(); } +inline bool JSObject::IsJSAPILinkedListIterator() const +{ + return GetJSHClass()->IsJSAPILinkedListIterator(); +} + +inline bool JSObject::IsJSAPIListIterator() const +{ + return GetJSHClass()->IsJSAPIListIterator(); +} + inline bool JSObject::IsJSPrimitiveRef() const { return GetJSHClass()->IsJsPrimitiveRef(); diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h index be3e2b4d..ffe8c978 100644 --- a/ecmascript/js_object.h +++ b/ecmascript/js_object.h @@ -542,6 +542,8 @@ public: bool IsJSAPIArrayListIterator() const; bool IsJSAPIStackIterator() const; bool IsJSAPIVectorIterator() const; + bool IsJSAPILinkedListIterator() const; + bool IsJSAPIListIterator() 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 dd8660db..6986b740 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -626,6 +626,26 @@ inline bool JSTaggedValue::IsJSAPIVector() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIVector(); } +inline bool JSTaggedValue::IsJSAPIList() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIList(); +} + +inline bool JSTaggedValue::IsJSAPILinkedList() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPILinkedList(); +} + +inline bool JSTaggedValue::IsJSAPILinkedListIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPILinkedListIterator(); +} + +inline bool JSTaggedValue::IsJSAPIListIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIListIterator(); +} + inline bool JSTaggedValue::IsSpecialContainer() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsSpecialContainer(); diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp index c94dcb15..5a94d046 100644 --- a/ecmascript/js_tagged_value.cpp +++ b/ecmascript/js_tagged_value.cpp @@ -18,13 +18,15 @@ #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_api_arraylist.h" #include "ecmascript/js_api_deque.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_list.h" #include "ecmascript/js_api_plain_array.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_api_vector.h" +#include "ecmascript/js_array.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_primitive_ref.h" #include "ecmascript/js_proxy.h" @@ -941,6 +943,14 @@ bool JSTaggedValue::HasContainerProperty(JSThread *thread, const JSHandle::Cast(obj)->Has(key.GetTaggedValue()); } + case JSType::JS_API_LIST: { + JSHandle list = JSHandle::Cast(obj); + return list->Has(key.GetTaggedValue()); + } + case JSType::JS_API_LINKED_LIST: { + JSHandle linkedList = JSHandle::Cast(obj); + return linkedList->Has(key.GetTaggedValue()); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::HasProperty(thread, JSHandle(obj), key); @@ -975,6 +985,12 @@ JSHandle JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *threa case JSType::JS_API_STACK: { return JSAPIStack::OwnKeys(thread, JSHandle::Cast(obj)); } + case JSType::JS_API_LIST: { + return JSAPIList::OwnKeys(thread, JSHandle::Cast(obj)); + } + case JSType::JS_API_LINKED_LIST: { + return JSAPILinkedList::OwnKeys(thread, JSHandle::Cast(obj)); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); @@ -992,34 +1008,40 @@ JSHandle JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *threa bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, PropertyDescriptor &desc) { - auto *hclass = obj->GetTaggedObject()->GetClass(); - JSType jsType = hclass->GetObjectType(); - switch (jsType) { - case JSType::JS_API_ARRAY_LIST: { - return JSAPIArrayList::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); - } - case JSType::JS_API_QUEUE: { - return JSAPIQueue::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); - } - case JSType::JS_API_PLAIN_ARRAY: { - return JSAPIPlainArray::GetOwnProperty(thread, 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); - } - case JSType::JS_API_VECTOR: { - return JSAPIVector::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); - } - default: { - UNREACHABLE(); + if (key->IsInteger()) { + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + switch (jsType) { + case JSType::JS_API_ARRAY_LIST: { + return JSAPIArrayList::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_QUEUE: { + return JSAPIQueue::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_DEQUE: { + return JSAPIDeque::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_STACK: { + return JSAPIStack::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_LIST: { + return JSAPIList::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_LINKED_LIST: { + return JSAPILinkedList::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_PLAIN_ARRAY: { + return JSAPIPlainArray::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_VECTOR: { + return JSAPIVector::GetOwnProperty(thread, JSHandle::Cast(obj), key); + } + default: { + return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); + } } + } else { + return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); } return false; } @@ -1041,12 +1063,31 @@ JSHandle JSTaggedValue::ToNumeric(JSThread *thread, JSHandle &obj, const JSHandle &key) { - auto *hclass = obj->GetTaggedObject()->GetClass(); - JSType jsType = hclass->GetObjectType(); - if (key->IsNumber()) { + if (key->IsInteger()) { + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); switch (jsType) { - case JSType::JS_API_PLAIN_ARRAY: + case JSType::JS_API_ARRAY_LIST: { + return JSAPIArrayList::GetProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_LIST: { + return JSAPIList::GetProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_LINKED_LIST: { + return JSAPILinkedList::GetProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_QUEUE: { + return JSAPIQueue::GetProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_DEQUE: { + return JSAPIDeque::GetProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_STACK: { + return JSAPIStack::GetProperty(thread, JSHandle::Cast(obj), key); + } + case JSType::JS_API_PLAIN_ARRAY: { return JSAPIPlainArray::GetProperty(thread, JSHandle::Cast(obj), key); + } default: { return JSObject::GetProperty(thread, JSHandle(obj), key); } diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index 80619948..9118050b 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -583,6 +583,10 @@ public: bool IsJSAPIDequeIterator() const; bool IsJSAPIStack() const; bool IsJSAPIStackIterator() const; + bool IsJSAPIList() const; + bool IsJSAPILinkedList() const; + bool IsJSAPIListIterator() const; + bool IsJSAPILinkedListIterator() const; bool IsSpecialContainer() const; bool IsPrototypeHandler() const; diff --git a/ecmascript/mem/object_xray.h b/ecmascript/mem/object_xray.h index 6268adaf..2484939a 100644 --- a/ecmascript/mem/object_xray.h +++ b/ecmascript/mem/object_xray.h @@ -30,6 +30,10 @@ #include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_api_deque.h" #include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_api_list.h" +#include "ecmascript/js_api_list_iterator.h" #include "ecmascript/js_api_plain_array.h" #include "ecmascript/js_api_plain_array_iterator.h" #include "ecmascript/js_api_queue.h" @@ -484,6 +488,18 @@ public: case JSType::JS_API_VECTOR_ITERATOR: JSAPIVectorIterator::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::JS_API_LIST: + JSAPIList::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_LINKED_LIST: + JSAPILinkedList::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_LIST_ITERATOR: + JSAPIListIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_LINKED_LIST_ITERATOR: + JSAPILinkedListIterator::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 cf587103..effa0444 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -45,6 +45,10 @@ #include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_api_deque.h" #include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_api_list.h" +#include "ecmascript/js_api_list_iterator.h" #include "ecmascript/js_api_plain_array.h" #include "ecmascript/js_api_plain_array_iterator.h" #include "ecmascript/js_api_queue.h" @@ -61,8 +65,6 @@ #include "ecmascript/js_array.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_arraybuffer.h" -#include "ecmascript/js_api_arraylist.h" -#include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_async_function.h" #include "ecmascript/js_bigint.h" #include "ecmascript/js_collator.h" @@ -107,6 +109,7 @@ #include "ecmascript/record.h" #include "ecmascript/shared_mm/shared_mm.h" #include "ecmascript/symbol_table.h" +#include "ecmascript/tagged_list.h" #include "ecmascript/tagged_tree.h" #include "ecmascript/template_map.h" #include "ecmascript/ts_types/ts_obj_layout_info.h" @@ -1113,6 +1116,12 @@ void ObjectFactory::InitializeJSObject(const JSHandle &obj, const JSHa case JSType::JS_API_VECTOR: JSAPIVector::Cast(*obj)->SetLength(0); break; + case JSType::JS_API_LIST: + JSAPIList::Cast(*obj)->SetSingleList(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_API_LINKED_LIST: + JSAPILinkedList::Cast(*obj)->SetDoubleList(thread_, JSTaggedValue::Undefined()); + break; case JSType::JS_ASYNC_FUNC_OBJECT: JSAsyncFuncObject::Cast(*obj)->SetGeneratorContext(thread_, JSTaggedValue::Undefined()); JSAsyncFuncObject::Cast(*obj)->SetResumeResult(thread_, JSTaggedValue::Undefined()); @@ -2941,9 +2950,8 @@ JSHandle ObjectFactory::NewJSAPIDequeIterator(const JSHandle return iter; } -JSHandle ObjectFactory::CopyQueue(const JSHandle &old, uint32_t oldLength, - uint32_t newLength, [[maybe_unused]] uint32_t front, - [[maybe_unused]] uint32_t tail) +JSHandle ObjectFactory::CopyQueue(const JSHandle &old, uint32_t newLength, + uint32_t front, uint32_t tail) { NewObjectHook(); size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); @@ -2953,11 +2961,17 @@ JSHandle ObjectFactory::CopyQueue(const JSHandle &old, newArray->InitializeWithSpecialValue(JSTaggedValue::Hole(), newLength); newArray->SetLength(newLength); - for (uint32_t i = 0; i < oldLength; i++) { - JSTaggedValue value = old->Get(i); - newArray->Set(thread_, i, value); + uint32_t curIndex = front; + // newIndex use in new TaggedArray, 0 : New TaggedArray index + uint32_t newIndex = 0; + uint32_t oldCapacity = old->GetLength(); + while (curIndex != tail) { + JSTaggedValue value = old->Get(curIndex); + newArray->Set(thread_, newIndex, value); + ASSERT(oldCapacity != 0); + curIndex = (curIndex + 1) % oldCapacity; + newIndex = newIndex + 1; } - return newArray; } @@ -3042,6 +3056,48 @@ JSHandle ObjectFactory::NewJSAPIVectorIterator(const JSHand return iter; } +JSHandle ObjectFactory::NewJSAPILinkedListIterator(const JSHandle &linkedList) +{ + NewObjectHook(); + JSHandle proto(thread_, thread_->GlobalConstants()->GetLinkedListIteratorPrototype()); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle dynHandle(globalConst->GetHandledJSAPILinkedListIteratorClass()); + dynHandle->SetPrototype(thread_, proto); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedLinkedList(thread_, linkedList->GetDoubleList()); + iter->SetNextIndex(0); + return iter; +} + +JSHandle ObjectFactory::NewJSAPIListIterator(const JSHandle &List) +{ + NewObjectHook(); + JSHandle proto(thread_, thread_->GlobalConstants()->GetListIteratorPrototype()); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle dynHandle(globalConst->GetHandledJSAPIListIteratorClass()); + dynHandle->SetPrototype(thread_, proto); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedList(thread_, List->GetSingleList()); + iter->SetNextIndex(0); + return iter; +} + +JSHandle ObjectFactory::NewJSAPIList() +{ + NewObjectHook(); + JSHandle function(thread_, thread_->GlobalConstants()->GetListFunction()); + return JSHandle::Cast(NewJSObjectByConstructor(JSHandle(function), function)); +} + +JSHandle ObjectFactory::NewJSAPILinkedList() +{ + NewObjectHook(); + JSHandle function(thread_, thread_->GlobalConstants()->GetLinkedListFunction()); + return JSHandle::Cast(NewJSObjectByConstructor(JSHandle(function), function)); +} + JSHandle ObjectFactory::NewImportEntry() { JSHandle defautValue = thread_->GlobalConstants()->GetHandledUndefined(); diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index 8a86c977..7a2deb65 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -114,6 +114,10 @@ class JSAPITreeSetIterator; class JSAPITreeMapIterator; class JSAPIVector; class JSAPIVectorIterator; +class JSAPILinkedList; +class JSAPIList; +class JSAPILinkedListIterator; +class JSAPIListIterator; class ModuleNamespace; class ImportEntry; class ExportEntry; @@ -442,13 +446,17 @@ public: JSHandle NewJSAPIPlainArrayIterator(const JSHandle &plainarray, IterationKind kind); JSHandle NewJSAPIArrayList(uint32_t capacity); - JSHandle CopyQueue(const JSHandle &old, uint32_t oldLength, - uint32_t newLength, uint32_t front, uint32_t tail); + JSHandle CopyQueue(const JSHandle &old, 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 NewJSAPIList(); + JSHandle NewJSAPILinkedList(); + JSHandle NewJSAPILinkedListIterator(const JSHandle &linkedList); + JSHandle NewJSAPIListIterator(const JSHandle &list); JSHandle NewJSAPITreeMapIterator(const JSHandle &map, IterationKind kind); JSHandle NewJSAPITreeSetIterator(const JSHandle &set, IterationKind kind); JSHandle NewJSAPIStackIterator(const JSHandle &stack); diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h index 01e54348..f7181831 100644 --- a/ecmascript/runtime_call_id.h +++ b/ecmascript/runtime_call_id.h @@ -684,7 +684,52 @@ namespace panda::ecmascript { V(Queue, Pop) \ V(Queue, ForEach) \ V(Queue, GetIteratorObj) \ - V(Queue, GetSize) + V(Queue, GetSize) \ + V(List, Constructor) \ + V(List, Add) \ + V(List, GetFirst) \ + V(List, GetLast) \ + V(List, Insert) \ + V(List, Clear) \ + V(List, RemoveByIndex) \ + V(List, Remove) \ + V(List, Has) \ + V(List, IsEmpty) \ + V(List, Get) \ + V(List, GetIndexOf) \ + V(List, GetLastIndexOf) \ + V(List, Set) \ + V(List, ForEach) \ + V(List, ReplaceAllElements) \ + V(List, GetIteratorObj) \ + V(List, Equal) \ + V(List, Sort) \ + V(List, ConvertToArray) \ + V(List, GetSubList) \ + V(List, Length) \ + V(LinkedList, Constructor) \ + V(LinkedList, Length) \ + V(LinkedList, Add) \ + V(LinkedList, GetFirst) \ + V(LinkedList, GetLast) \ + V(LinkedList, Insert) \ + V(LinkedList, AddFirst) \ + V(LinkedList, Clear) \ + V(LinkedList, Clone) \ + V(LinkedList, Has) \ + V(LinkedList, Get) \ + V(LinkedList, GetIndexOf) \ + V(LinkedList, GetLastIndexOf) \ + V(LinkedList, RemoveByIndex) \ + V(LinkedList, Remove) \ + V(LinkedList, RemoveFirst) \ + V(LinkedList, RemoveLast) \ + V(LinkedList, RemoveFirstFound) \ + V(LinkedList, RemoveLastFound) \ + V(LinkedList, Set) \ + V(LinkedList, ConvertToArray) \ + V(LinkedList, ForEach) \ + V(LinkedList, GetIteratorObj) #define ABSTRACT_OPERATION_LIST(V) \ V(JSTaggedValue, ToString) \ diff --git a/ecmascript/snapshot/mem/snapshot_processor.cpp b/ecmascript/snapshot/mem/snapshot_processor.cpp index af5ae525..077a7ac0 100644 --- a/ecmascript/snapshot/mem/snapshot_processor.cpp +++ b/ecmascript/snapshot/mem/snapshot_processor.cpp @@ -57,6 +57,8 @@ #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/containers/containers_arraylist.h" #include "ecmascript/containers/containers_deque.h" +#include "ecmascript/containers/containers_linked_list.h" +#include "ecmascript/containers/containers_list.h" #include "ecmascript/containers/containers_plainarray.h" #include "ecmascript/containers/containers_private.h" #include "ecmascript/containers/containers_queue.h" @@ -68,7 +70,10 @@ #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/global_env.h" +#include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_api_list_iterator.h" #include "ecmascript/js_api_plain_array_iterator.h" #include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_stack_iterator.h" @@ -76,7 +81,6 @@ #include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_api_vector_iterator.h" #include "ecmascript/js_array_iterator.h" -#include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_for_in_iterator.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_map_iterator.h" @@ -143,6 +147,8 @@ using TreeMap = containers::ContainersTreeMap; using TreeSet = containers::ContainersTreeSet; using Vector = containers::ContainersVector; using Queue = containers::ContainersQueue; +using List = containers::ContainersList; +using LinkedList = containers::ContainersLinkedList; using PlainArray = containers::ContainersPlainArray; using Deque = containers::ContainersDeque; using ContainerStack = panda::ecmascript::containers::ContainersStack; @@ -740,6 +746,51 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(ContainerStack::ForEach), reinterpret_cast(ContainerStack::GetLength), reinterpret_cast(JSAPIStackIterator::Next), + reinterpret_cast(List::ListConstructor), + reinterpret_cast(List::Add), + reinterpret_cast(List::GetFirst), + reinterpret_cast(List::GetLast), + reinterpret_cast(List::Insert), + reinterpret_cast(List::Clear), + reinterpret_cast(List::RemoveByIndex), + reinterpret_cast(List::Remove), + reinterpret_cast(List::Has), + reinterpret_cast(List::IsEmpty), + reinterpret_cast(List::Get), + reinterpret_cast(List::GetIndexOf), + reinterpret_cast(List::GetLastIndexOf), + reinterpret_cast(List::Set), + reinterpret_cast(List::ForEach), + reinterpret_cast(List::ReplaceAllElements), + reinterpret_cast(List::GetIteratorObj), + reinterpret_cast(List::Equal), + reinterpret_cast(List::Sort), + reinterpret_cast(List::ConvertToArray), + reinterpret_cast(List::GetSubList), + reinterpret_cast(List::Length), + reinterpret_cast(JSAPIListIterator::Next), + reinterpret_cast(LinkedList::LinkedListConstructor), + reinterpret_cast(LinkedList::Add), + reinterpret_cast(LinkedList::GetFirst), + reinterpret_cast(LinkedList::GetLast), + reinterpret_cast(LinkedList::Insert), + reinterpret_cast(LinkedList::AddFirst), + reinterpret_cast(LinkedList::Clear), + reinterpret_cast(LinkedList::Clone), + reinterpret_cast(LinkedList::Has), + reinterpret_cast(LinkedList::Get), + reinterpret_cast(LinkedList::GetIndexOf), + reinterpret_cast(LinkedList::GetLastIndexOf), + reinterpret_cast(LinkedList::RemoveByIndex), + reinterpret_cast(LinkedList::Remove), + reinterpret_cast(LinkedList::RemoveFirst), + reinterpret_cast(LinkedList::RemoveLast), + reinterpret_cast(LinkedList::RemoveFirstFound), + reinterpret_cast(LinkedList::RemoveLastFound), + reinterpret_cast(LinkedList::Set), + reinterpret_cast(LinkedList::ConvertToArray), + reinterpret_cast(LinkedList::ForEach), + reinterpret_cast(JSAPILinkedListIterator::Next), // not builtins method reinterpret_cast(JSFunction::PrototypeSetter), diff --git a/ecmascript/tagged_list.cpp b/ecmascript/tagged_list.cpp new file mode 100644 index 00000000..4219bc8d --- /dev/null +++ b/ecmascript/tagged_list.cpp @@ -0,0 +1,684 @@ +/* + * 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 "tagged_list.h" +#include "ecmascript/base/array_helper.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_number.h" +#include "object_factory.h" + +namespace panda::ecmascript { +template +JSHandle TaggedList::Create(const JSThread *thread, int numberOfNodes) +{ + ASSERT_PRINT(numberOfNodes > 0, "size must be a non-negative integer"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int length = ELEMENTS_START_INDEX + Derived::ENTRY_SIZE + numberOfNodes * Derived::ENTRY_SIZE; + JSHandle taggedArray = factory->NewTaggedArray(length); + auto taggedList = JSHandle::Cast(taggedArray); + JSTaggedValue data = JSTaggedValue(ELEMENTS_START_INDEX); + taggedList->SetNumberOfNodes(thread, 0); + taggedList->SetNumberOfDeletedNodes(thread, 0); + taggedList->SetElement(thread, HEAD_TABLE_INDEX, data); + taggedList->SetElement(thread, TAIL_TABLE_INDEX, data); + taggedList->SetElement(thread, ELEMENTS_START_INDEX, JSTaggedValue::Hole()); + taggedList->SetElement(thread, ELEMENTS_START_INDEX + NEXT_PTR_OFFSET, data); + return taggedList; +} + +template +void TaggedList::CopyArray(const JSThread *thread, JSHandle &taggedList) +{ + int prevDataIndex = ELEMENTS_START_INDEX; + int nextDataIndex = GetElement(ELEMENTS_START_INDEX + NEXT_PTR_OFFSET).GetInt(); + int nodeNum = 0; + int nodeLength = NumberOfNodes(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + while (nodeLength > nodeNum) { + int finalDataIndex = ELEMENTS_START_INDEX + Derived::ENTRY_SIZE + nodeNum * Derived::ENTRY_SIZE; + value.Update(GetElement(nextDataIndex)); + taggedList->InsertNode(thread, value, prevDataIndex, finalDataIndex); + prevDataIndex = finalDataIndex; + nextDataIndex = GetElement(nextDataIndex + NEXT_PTR_OFFSET).GetInt(); + taggedList->SetElement(thread, TAIL_TABLE_INDEX, JSTaggedValue(finalDataIndex)); + nodeNum++; + } + taggedList->SetNumberOfDeletedNodes(thread, 0); +} + +template +JSHandle TaggedList::GrowCapacity(const JSThread *thread, const JSHandle &taggedList) +{ + int actualNodeNum = taggedList->NumberOfNodes(); + int deleteNodeNum = taggedList->NumberOfDeletedNodes(); + int needCapacity = actualNodeNum + 1; + int taggedArrayLength = taggedList->GetCapacityFromTaggedArray(); + int actualArrayCapacity = (taggedArrayLength - ELEMENTS_START_INDEX - (deleteNodeNum + 1) * Derived::ENTRY_SIZE); + if (needCapacity * Derived::ENTRY_SIZE < actualArrayCapacity) { + return taggedList; + } + int newCapacity = actualNodeNum + (actualNodeNum >> 1UL); + JSHandle list = Create(thread, newCapacity < DEFAULT_ARRAY_LENGHT ? DEFAULT_ARRAY_LENGHT : newCapacity); + taggedList->CopyArray(thread, list); + return list; +} + +template +JSTaggedValue TaggedList::AddNode(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value, const int index, int prevDataIndex) +{ + JSHandle list = GrowCapacity(thread, taggedList); + int deleteNodeLength = list->NumberOfDeletedNodes(); + int nodeLength = list->NumberOfNodes(); + int finalDataIndex = ELEMENTS_START_INDEX + (nodeLength + 1 + deleteNodeLength) * Derived::ENTRY_SIZE; + + list->InsertNode(thread, value, prevDataIndex, finalDataIndex); + if (index == -1 || nodeLength == index) { + list->SetElement(thread, TAIL_TABLE_INDEX, JSTaggedValue(finalDataIndex)); + } + return list.GetTaggedValue(); +} + +template +void TaggedList::Clear(const JSThread *thread) +{ + int numberOfElements = NumberOfNodes(); + int deleteNodesNum = NumberOfDeletedNodes(); + SetElement(thread, TAIL_TABLE_INDEX, JSTaggedValue(ELEMENTS_START_INDEX)); + SetElement(thread, ELEMENTS_START_INDEX + NEXT_PTR_OFFSET, JSTaggedValue(ELEMENTS_START_INDEX)); + SetNumberOfNodes(thread, 0); + SetNumberOfDeletedNodes(thread, numberOfElements + deleteNodesNum); +} + +template +JSTaggedValue TaggedList::TaggedListToArray(const JSThread *thread, const JSHandle &list) +{ + uint32_t length = static_cast(list->NumberOfNodes()); + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewJSArray(); + array->SetArrayLength(thread, length); + JSHandle arrayElements(thread, array->GetElements()); + uint32_t oldLength = arrayElements->GetLength(); + JSHandle newElements = + thread->GetEcmaVM()->GetFactory()->CopyArray(arrayElements, oldLength, length); + for (uint32_t i = 0; i < length; i++) { + newElements->Set(thread, i, list->Get(i)); + } + array->SetElements(thread, newElements); + return array.GetTaggedValue(); +} + +template +JSHandle TaggedList::OwnKeys(JSThread *thread, const JSHandle &list) +{ + uint32_t length = static_cast(list->NumberOfNodes()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys = factory->NewTaggedArray(length); + + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue elementData = JSTaggedValue(i); + keys->Set(thread, i, elementData); + } + + return keys; +} + +template +int TaggedList::FindIndexByElement(const JSTaggedValue &element) +{ + int dataIndex = ELEMENTS_START_INDEX; + int nextDataIndex = GetElement(dataIndex + NEXT_PTR_OFFSET).GetInt(); + int nodeSum = 0; + while (nextDataIndex != ELEMENTS_START_INDEX) { + dataIndex = nextDataIndex; + JSTaggedValue data = GetElement(dataIndex); + nextDataIndex = GetElement(nextDataIndex + NEXT_PTR_OFFSET).GetInt(); + if (JSTaggedValue::SameValue(data, element)) { + return nodeSum; + } + nodeSum++; + } + return -1; +} + +template +int TaggedList::FindLastIndexByElement(const JSTaggedValue &element) +{ + int dataIndex = ELEMENTS_START_INDEX; + int nextIndex = GetElement(dataIndex + NEXT_PTR_OFFSET).GetInt(); + int nodeSum = 0; + int lastIndex = -1; + while (nextIndex != ELEMENTS_START_INDEX) { + dataIndex = nextIndex; + JSTaggedValue data = GetElement(dataIndex); + if (JSTaggedValue::SameValue(data, element)) { + lastIndex = nodeSum; + } + nextIndex = GetElement(nextIndex + NEXT_PTR_OFFSET).GetInt(); + nodeSum++; + } + return lastIndex; +} + +template +int TaggedList::FindDataIndexByNodeIndex(int index) const +{ + int dataIndex = ELEMENTS_START_INDEX; + int nextIndex = GetElement(dataIndex + NEXT_PTR_OFFSET).GetInt(); + int nodeSum = 0; + while (nextIndex != ELEMENTS_START_INDEX) { + dataIndex = nextIndex; + if (nodeSum == index) { + return dataIndex; + } + nextIndex = GetElement(nextIndex + NEXT_PTR_OFFSET).GetInt(); + nodeSum++; + } + return -1; +} + +template +void TaggedList::RemoveNode(JSThread *thread, int prevDataIndex) +{ + int tailTableIndex = GetElement(TAIL_TABLE_INDEX).GetInt(); + if (tailTableIndex != GetElement(HEAD_TABLE_INDEX).GetInt()) { + int dataIndex = GetElement(prevDataIndex + NEXT_PTR_OFFSET).GetInt(); + int nextDataIndex = GetElement(dataIndex + NEXT_PTR_OFFSET).GetInt(); + if (dataIndex == tailTableIndex) { + SetElement(thread, TAIL_TABLE_INDEX, JSTaggedValue(prevDataIndex)); + } + if (std::is_same_v) { + SetElement(thread, nextDataIndex + PREV_PTR_OFFSET, JSTaggedValue(prevDataIndex)); + } + SetElement(thread, dataIndex, JSTaggedValue::Hole()); + SetElement(thread, prevDataIndex + NEXT_PTR_OFFSET, JSTaggedValue(nextDataIndex)); + SetNumberOfNodes(thread, NumberOfNodes() - 1); + SetNumberOfDeletedNodes(thread, NumberOfDeletedNodes() + 1); + } +} + +template +int TaggedList::FindPrevNodeByIndex(int index) const +{ + int prevDataIndex = ELEMENTS_START_INDEX; + int nodeSum = 0; + int len = GetElement(NUMBER_OF_NODE_INDEX).GetInt(); + while (nodeSum <= len) { + if (nodeSum == index) { + return prevDataIndex; + } + prevDataIndex = GetElement(prevDataIndex + NEXT_PTR_OFFSET).GetInt(); + nodeSum++; + } + return -1; +} + +template +int TaggedList::FindPrevNodeByValue(const JSTaggedValue &element) +{ + int dataIndex = ELEMENTS_START_INDEX; + int nodeSum = 0; + int len = GetElement(NUMBER_OF_NODE_INDEX).GetInt(); + while (nodeSum <= len) { + int nextDataIndex = GetElement(dataIndex + NEXT_PTR_OFFSET).GetInt(); + JSTaggedValue data = GetElement(nextDataIndex); + if (JSTaggedValue::SameValue(data, element)) { + return dataIndex; + } + dataIndex = nextDataIndex; + nodeSum++; + } + return -1; +} + +template +JSTaggedValue TaggedList::FindElementByIndex(int index) const +{ + int dataIndex = ELEMENTS_START_INDEX; + int nextIndex = GetElement(dataIndex + NEXT_PTR_OFFSET).GetInt(); + int nodeSum = 0; + while (nextIndex != ELEMENTS_START_INDEX) { + dataIndex = nextIndex; + JSTaggedValue dataValue = GetElement(dataIndex); + if (nodeSum == index) { + return dataValue; + } + nextIndex = GetElement(nextIndex + NEXT_PTR_OFFSET).GetInt(); + nodeSum++; + } + return JSTaggedValue::Undefined(); +} + +template +JSTaggedValue TaggedList::RemoveByIndex(JSThread *thread, const int &index) +{ + int prevDataIndex = FindPrevNodeByIndex(index); + int curDataIndex = GetElement(prevDataIndex + NEXT_PTR_OFFSET).GetInt(); + JSTaggedValue data = GetElement(curDataIndex); + RemoveNode(thread, prevDataIndex); + return data; +} + +// TaggedSingleList +JSTaggedValue TaggedSingleList::Create(const JSThread *thread, int numberOfElements) +{ + return TaggedList::Create(thread, numberOfElements).GetTaggedValue(); +} + +JSTaggedValue TaggedSingleList::Add(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value) +{ + int prevDataIndex = taggedList->GetElement(TAIL_TABLE_INDEX).GetInt(); + return TaggedList::AddNode(thread, taggedList, value, -1, prevDataIndex); +} + +JSTaggedValue TaggedSingleList::ConvertToArray(const JSThread *thread, const JSHandle &taggedList) +{ + return JSTaggedValue(TaggedList::TaggedListToArray(thread, taggedList)); +} + +JSTaggedValue TaggedSingleList::Insert(JSThread *thread, const JSHandle &taggedList, + const JSHandle &value, const int index) +{ + int tailIndex = taggedList->GetElement(TAIL_TABLE_INDEX).GetInt(); + int prevDataIndex = (index == -1) ? tailIndex : taggedList->FindPrevNodeByIndex(index); + return TaggedList::AddNode(thread, taggedList, value, index, prevDataIndex); +} + +void TaggedSingleList::InsertNode(const JSThread *thread, const JSHandle &value, const int prevDataIndex, + const int finalDataIndex) +{ + int prevNextIndex = prevDataIndex + NEXT_PTR_OFFSET; + int nextDataIndex = GetElement(prevNextIndex).GetInt(); + SetElement(thread, prevNextIndex, JSTaggedValue(finalDataIndex)); + SetElement(thread, finalDataIndex, value.GetTaggedValue()); + SetElement(thread, finalDataIndex + 1, JSTaggedValue(nextDataIndex)); + SetNumberOfNodes(thread, NumberOfNodes() + 1); +} + +bool TaggedSingleList::Has(const JSTaggedValue &element) +{ + int dataIndex = FindIndexByElement(element); + return dataIndex != -1; +} + +bool TaggedSingleList::IsEmpty() const +{ + return NumberOfNodes() == 0; +} + +JSTaggedValue TaggedSingleList::Get(const int index) +{ + return FindElementByIndex(index); +} + +int TaggedSingleList::GetIndexOf(const JSTaggedValue &element) +{ + return FindIndexByElement(element); +} + +int TaggedSingleList::GetLastIndexOf(const JSTaggedValue &element) +{ + return FindLastIndexByElement(element); +} + +JSTaggedValue TaggedSingleList::Set(JSThread *thread, const JSHandle &taggedList, + const int index, const JSHandle &value) +{ + int dataIndex = taggedList->FindDataIndexByNodeIndex(index); + if (dataIndex == -1) { + return taggedList.GetTaggedValue(); + } + taggedList->SetElement(thread, dataIndex, value.GetTaggedValue()); + return taggedList.GetTaggedValue(); +} + +JSTaggedValue TaggedSingleList::ReplaceAllElements(JSThread *thread, const JSHandle &thisHandle, + const JSHandle &callbackFn, + const JSHandle &thisArg, + const JSHandle &taggedList) +{ + int length = taggedList->NumberOfNodes(); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + for (int k = 0; k < length; k++) { + JSTaggedValue kValue = taggedList->Get(k); + JSTaggedValue key = JSTaggedValue(k); + EcmaRuntimeCallInfo info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, 3); // 3:three args + info.SetCallArg(kValue, key, thisHandle.GetTaggedValue()); + JSTaggedValue funcResult = JSFunction::Call(&info); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + JSHandle funcResultValue = JSHandle(thread, funcResult); + TaggedSingleList::Set(thread, taggedList, k, funcResultValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue TaggedSingleList::Sort(JSThread *thread, const JSHandle &callbackFn, + const JSHandle &taggedList) +{ + int length = taggedList->NumberOfNodes(); + JSMutableHandle firstValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle secondValue(thread, JSTaggedValue::Undefined()); + for (int i = 0; i < length; i++) { + for (int j = i + 1; j < length; j++) { + int firstIndex = taggedList->FindDataIndexByNodeIndex(i); + int secondIndex = taggedList->FindDataIndexByNodeIndex(j); + firstValue.Update(taggedList->GetElement(firstIndex)); + secondValue.Update(taggedList->GetElement(secondIndex)); + int32_t compareResult = base::ArrayHelper::SortCompare(thread, callbackFn, firstValue, secondValue); + if (compareResult > 0) { + taggedList->SetElement(thread, firstIndex, secondValue.GetTaggedValue()); + taggedList->SetElement(thread, secondIndex, firstValue.GetTaggedValue()); + } + } + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue TaggedSingleList::GetSubList(JSThread *thread, const JSHandle &taggedList, + const int fromIndex, const int toIndex, + const JSHandle &subList) +{ + int dataIndex = taggedList->FindDataIndexByNodeIndex(fromIndex); + int endIndex = taggedList->FindDataIndexByNodeIndex(toIndex); + int preDataIndex = ELEMENTS_START_INDEX; + int num = 0; + JSMutableHandle dataHandle(thread, JSTaggedValue::Undefined()); + JSHandle nextHandle(thread, JSTaggedValue(ELEMENTS_START_INDEX)); + while (dataIndex != endIndex) { + int elementDataIndex = ELEMENTS_START_INDEX + TaggedSingleList::ENTRY_SIZE + num * TaggedSingleList::ENTRY_SIZE; + dataHandle.Update(taggedList->GetElement(dataIndex)); + subList->SetElement(thread, preDataIndex + NEXT_PTR_OFFSET, JSTaggedValue(elementDataIndex)); + subList->SetElement(thread, elementDataIndex, dataHandle.GetTaggedValue()); + subList->SetElement(thread, elementDataIndex + NEXT_PTR_OFFSET, nextHandle.GetTaggedValue()); + subList->SetElement(thread, TAIL_TABLE_INDEX, JSTaggedValue(elementDataIndex)); + subList->SetNumberOfNodes(thread, num + 1); + dataIndex = taggedList->GetElement(dataIndex + NEXT_PTR_OFFSET).GetInt(); + preDataIndex = elementDataIndex; + num++; + } + subList->SetNumberOfDeletedNodes(thread, 0); + + return subList.GetTaggedValue(); +} + +JSTaggedValue TaggedSingleList::Equal(const JSHandle &compareList) +{ + int compareListLength = compareList->NumberOfNodes(); + if (compareListLength != NumberOfNodes()) { + return JSTaggedValue::False(); + } + int nodeSum = 0; + while (nodeSum < compareListLength) { + JSTaggedValue compareValue = compareList->Get(nodeSum); + JSTaggedValue value = Get(nodeSum); + if (compareValue != value) { + return JSTaggedValue::False(); + } + nodeSum++; + } + return JSTaggedValue::True(); +} + +void TaggedSingleList::Clear(const JSThread *thread) +{ + TaggedList::Clear(thread); +} + +JSTaggedValue TaggedSingleList::RemoveByIndex(JSThread *thread, const int &index) +{ + return TaggedList::RemoveByIndex(thread, index); +} + +JSTaggedValue TaggedSingleList::Remove(JSThread *thread, const JSTaggedValue &element) +{ + int prevDataIndex = FindPrevNodeByValue(element); + if (prevDataIndex == -1) { + return JSTaggedValue::False(); + } + RemoveNode(thread, prevDataIndex); + return JSTaggedValue::True(); +} + +JSHandle TaggedSingleList::OwnKeys(JSThread *thread, const JSHandle &taggedList) +{ + return TaggedList::OwnKeys(thread, taggedList); +} + +// TaggedDoubleList +JSTaggedValue TaggedDoubleList::Create(const JSThread *thread, int numberOfElements) +{ + JSHandle taggedList = TaggedList::Create(thread, numberOfElements); + taggedList->SetElement(thread, ELEMENTS_START_INDEX + PREV_PTR_OFFSET, JSTaggedValue(ELEMENTS_START_INDEX)); + return taggedList.GetTaggedValue(); +} + +JSTaggedValue TaggedDoubleList::Add(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value) +{ + int prevDataIndex = taggedList->GetElement(TAIL_TABLE_INDEX).GetInt(); + return TaggedList::AddNode(thread, taggedList, value, -1, prevDataIndex); +} + +JSTaggedValue TaggedDoubleList::AddFirst(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value) +{ + int prevDataIndex = taggedList->FindPrevNodeByIndex(0); + return TaggedList::AddNode(thread, taggedList, value, 0, prevDataIndex); +} + +JSTaggedValue TaggedDoubleList::ConvertToArray(const JSThread *thread, const JSHandle &taggedList) +{ + return JSTaggedValue(TaggedList::TaggedListToArray(thread, taggedList)); +} + +JSTaggedValue TaggedDoubleList::Insert(JSThread *thread, const JSHandle &taggedList, + const JSHandle &value, const int index) +{ + int prevDataIndex = 0; + int len = taggedList->NumberOfNodes(); + int leftNodeLen = len - 1 - index; + if (leftNodeLen == -1) { + prevDataIndex = taggedList->GetElement(TAIL_TABLE_INDEX).GetInt(); + } else { + // 2 : 2 MEANS the half + if ((len / 2) > index) { + prevDataIndex = taggedList->FindPrevNodeByIndex(index); + } else { + prevDataIndex = taggedList->FindPrevNodeByIndexAtLast(leftNodeLen); + } + } + return TaggedList::AddNode(thread, taggedList, value, index, prevDataIndex); +} + +void TaggedDoubleList::InsertNode(const JSThread *thread, const JSHandle &value, const int prevDataIndex, + const int finalDataIndex) +{ + int prevNextIndex = prevDataIndex + NEXT_PTR_OFFSET; + int nextDataIndex = GetElement(prevNextIndex).GetInt(); + int nextPrevIndex = nextDataIndex + PREV_PTR_OFFSET; + SetElement(thread, prevNextIndex, JSTaggedValue(finalDataIndex)); + SetElement(thread, nextPrevIndex, JSTaggedValue(finalDataIndex)); + SetElement(thread, finalDataIndex, value.GetTaggedValue()); + SetElement(thread, finalDataIndex + NEXT_PTR_OFFSET, JSTaggedValue(nextDataIndex)); + SetElement(thread, finalDataIndex + PREV_PTR_OFFSET, JSTaggedValue(prevDataIndex)); + SetNumberOfNodes(thread, NumberOfNodes() + 1); +} + +bool TaggedDoubleList::Has(const JSTaggedValue &element) +{ + int dataIndex = FindIndexByElement(element); + return dataIndex != -1; +} + +JSTaggedValue TaggedDoubleList::Get(const int index) +{ + int len = NumberOfNodes(); + // 2 : 2 MEANS the half + if (len / 2 > index) { + return FindElementByIndex(index); + } else { + return FindElementByIndexAtLast(index); + } +} + +int TaggedDoubleList::GetIndexOf(const JSTaggedValue &element) +{ + return FindIndexByElement(element); +} + +int TaggedDoubleList::GetLastIndexOf(const JSTaggedValue &element) +{ + return FindLastIndexByElement(element); +} + +JSTaggedValue TaggedDoubleList::Set(JSThread *thread, const JSHandle &taggedList, const int index, + const JSHandle &value) +{ + int nodeLength = taggedList->NumberOfNodes(); + if (index < 0 || index >= nodeLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Set index out-of-bounds", JSTaggedValue::Exception()); + } + int dataIndex = taggedList->FindDataIndexByNodeIndex(index); + if (dataIndex == -1) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Set index not exist", JSTaggedValue::Exception()); + } + taggedList->SetElement(thread, dataIndex, value.GetTaggedValue()); + return taggedList.GetTaggedValue(); +} + +void TaggedDoubleList::Clear(const JSThread *thread) +{ + TaggedList::Clear(thread); + SetElement(thread, ELEMENTS_START_INDEX + PREV_PTR_OFFSET, JSTaggedValue(ELEMENTS_START_INDEX)); +} + +JSTaggedValue TaggedDoubleList::RemoveFirst(JSThread *thread) +{ + int prevDataIndex = FindPrevNodeByIndex(0); + int firstDataIndex = GetElement(ELEMENTS_START_INDEX + NEXT_PTR_OFFSET).GetInt(); + JSTaggedValue firstData = GetElement(firstDataIndex); + RemoveNode(thread, prevDataIndex); + return firstData; +} + +JSTaggedValue TaggedDoubleList::RemoveLast(JSThread *thread) +{ + int prevDataIndex = FindPrevNodeByIndex(NumberOfNodes() - 1); + int lastDataIndex = GetElement(ELEMENTS_START_INDEX + 2).GetInt(); + JSTaggedValue lastData = GetElement(lastDataIndex); + RemoveNode(thread, prevDataIndex); + return lastData; +} + +JSTaggedValue TaggedDoubleList::RemoveByIndex(JSThread *thread, const int &index) +{ + return TaggedList::RemoveByIndex(thread, index); +} + +JSTaggedValue TaggedDoubleList::Remove(JSThread *thread, const JSTaggedValue &element) +{ + int prevDataIndex = FindPrevNodeByValue(element); + if (prevDataIndex == -1) { + return JSTaggedValue::False(); + } + RemoveNode(thread, prevDataIndex); + return JSTaggedValue::True(); +} + +JSTaggedValue TaggedDoubleList::RemoveFirstFound(JSThread *thread, const JSHandle &taggedList, + const JSTaggedValue &element) +{ + int prevDataIndex = taggedList->FindPrevNodeByValue(element); + if (prevDataIndex == -1) { + THROW_RANGE_ERROR_AND_RETURN(thread, "this element is not exist in this container", JSTaggedValue::Exception()); + } + taggedList->RemoveNode(thread, prevDataIndex); + return JSTaggedValue::True(); +} + +JSTaggedValue TaggedDoubleList::RemoveLastFound(JSThread *thread, const JSHandle &taggedList, + const JSTaggedValue &element) +{ + int prevDataIndex = taggedList->FindPrevNodeByValueAtLast(element); + if (prevDataIndex == -1) { + THROW_RANGE_ERROR_AND_RETURN(thread, "this element is not exist in this container", JSTaggedValue::Exception()); + } + taggedList->RemoveNode(thread, prevDataIndex); + return JSTaggedValue::True(); +} + +JSHandle TaggedDoubleList::OwnKeys(JSThread *thread, const JSHandle &taggedList) +{ + return TaggedList::OwnKeys(thread, taggedList); +} + +JSTaggedValue TaggedDoubleList::FindElementByIndexAtLast(int index) const +{ + int dataIndex = ELEMENTS_START_INDEX; + int preDataIndex = GetElement(dataIndex + PREV_PTR_OFFSET).GetInt(); + int nodeSum = GetElement(NUMBER_OF_NODE_INDEX).GetInt() - 1; + while (preDataIndex != ELEMENTS_START_INDEX) { + dataIndex = preDataIndex; + JSTaggedValue dataValue = GetElement(dataIndex); + if (nodeSum == index) { + return dataValue; + } + preDataIndex = GetElement(preDataIndex + PREV_PTR_OFFSET).GetInt(); + nodeSum--; + } + return JSTaggedValue::Undefined(); +} + +int TaggedDoubleList::FindPrevNodeByIndexAtLast(const int index) const +{ + int prevDataIndex = GetElement(ELEMENTS_START_INDEX + PREV_PTR_OFFSET).GetInt(); + int nodeSum = 0; + int len = GetElement(NUMBER_OF_NODE_INDEX).GetInt(); + while (nodeSum <= len) { + int prePreDataIndex = GetElement(prevDataIndex + PREV_PTR_OFFSET).GetInt(); + if (nodeSum == index) { + return prePreDataIndex; + } + prevDataIndex = prePreDataIndex; + nodeSum++; + } + return -1; +} + +int TaggedDoubleList::FindPrevNodeByValueAtLast(const JSTaggedValue &element) +{ + int prevDataIndex = GetElement(ELEMENTS_START_INDEX + PREV_PTR_OFFSET).GetInt(); + int nodeSum = 0; + int len = GetElement(NUMBER_OF_NODE_INDEX).GetInt(); + while (nodeSum <= len) { + int prePreDataIndex = GetElement(prevDataIndex + PREV_PTR_OFFSET).GetInt(); + JSTaggedValue data = GetElement(prevDataIndex); + if (JSTaggedValue::SameValue(data, element)) { + return prePreDataIndex; + } + prevDataIndex = prePreDataIndex; + nodeSum++; + } + return -1; +} +} // namespace panda::ecmascript diff --git a/ecmascript/tagged_list.h b/ecmascript/tagged_list.h new file mode 100644 index 00000000..daa20117 --- /dev/null +++ b/ecmascript/tagged_list.h @@ -0,0 +1,190 @@ +/* + * 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_TAGGED_LIST_H +#define ECMASCRIPT_TAGGED_LIST_H + +#include "ecmascript/js_tagged_value.h" +#include "js_handle.h" +#include "js_symbol.h" +#include "js_tagged_number.h" +#include "tagged_array.h" + +namespace panda::ecmascript { +template +class TaggedList : public TaggedArray { +public: + static const int NUMBER_OF_NODE_INDEX = 0; + static const int NUMBER_OF_DELETED_NODES_INDEX = 1; + static const int HEAD_TABLE_INDEX = 2; + static const int TAIL_TABLE_INDEX = 3; + static const int ELEMENTS_START_INDEX = 4; + static const int DEFAULT_ARRAY_LENGHT = 10; + static const int NEXT_PTR_OFFSET = 1; + static const int PREV_PTR_OFFSET = 2; + + static JSHandle Create(const JSThread *thread, int numberOfNodes = DEFAULT_ARRAY_LENGHT); + static JSHandle GrowCapacity(const JSThread *thread, const JSHandle &taggedList); + static JSTaggedValue AddNode(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value, const int index, int prevDataIndex); + static JSTaggedValue TaggedListToArray(const JSThread *thread, const JSHandle &taggedList); + static JSHandle OwnKeys(JSThread *thread, const JSHandle &taggedList); + void CopyArray(const JSThread *thread, JSHandle &taggedList); + void Clear(const JSThread *thread); + JSTaggedValue FindElementByIndex(int index) const; + int FindIndexByElement(const JSTaggedValue &element); + int FindLastIndexByElement(const JSTaggedValue &element); + int FindDataIndexByNodeIndex(int index) const; + void RemoveNode(JSThread *thread, int prevDataIndex); + int FindPrevNodeByIndex(int index) const; + int FindPrevNodeByValue(const JSTaggedValue &element); + JSTaggedValue RemoveByIndex(JSThread *thread, const int &index); + inline int Length() + { + return NumberOfNodes(); + } + + inline JSTaggedValue GetFirst() + { + int firstDataIndex = GetElement(ELEMENTS_START_INDEX + NEXT_PTR_OFFSET).GetInt(); + return GetElement(firstDataIndex); + } + + inline JSTaggedValue GetLast() + { + int lastDataIndex = GetElement(TAIL_TABLE_INDEX).GetInt(); + return GetElement(lastDataIndex); + } + + inline int GetCapacityFromTaggedArray() + { + return static_cast(GetLength()); + } + + inline void SetElement(const JSThread *thread, int index, const JSTaggedValue &element) + { + ASSERT(index >= 0 && index < static_cast(GetLength())); + Set(thread, index, element); + } + + inline JSTaggedValue GetElement(int index) const + { + ASSERT(index >= 0 && index < static_cast(GetLength())); + return Get(index); + } + + inline int NumberOfNodes() const + { + return Get(NUMBER_OF_NODE_INDEX).GetInt(); + } + + inline int NumberOfDeletedNodes() const + { + return Get(NUMBER_OF_DELETED_NODES_INDEX).GetInt(); + } + + inline void SetNumberOfDeletedNodes(const JSThread *thread, int nod) + { + Set(thread, NUMBER_OF_DELETED_NODES_INDEX, JSTaggedValue(nod)); + } + + inline void SetNumberOfNodes(const JSThread *thread, int nof) + { + Set(thread, NUMBER_OF_NODE_INDEX, JSTaggedValue(nof)); + } +}; + +class TaggedSingleList : public TaggedList { +public: + static const int ENTRY_SIZE = 2; + static TaggedSingleList *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + + static JSTaggedValue Create(const JSThread *thread, int numberOfElements = TaggedSingleList::DEFAULT_ARRAY_LENGHT); + static JSTaggedValue Add(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value); + static JSTaggedValue Insert(JSThread *thread, const JSHandle &taggedList, + const JSHandle &value, const int index); + static JSTaggedValue Set(JSThread *thread, const JSHandle &taggedList, + const int index, const JSHandle &value); + static JSTaggedValue ReplaceAllElements(JSThread *thread, const JSHandle &thisHandle, + const JSHandle &callbackFn, + const JSHandle &thisArg, + const JSHandle &taggedList); + static JSTaggedValue Sort(JSThread *thread, const JSHandle &callbackFn, + const JSHandle &taggedList); + static JSTaggedValue ConvertToArray(const JSThread *thread, const JSHandle &taggedList); + static JSTaggedValue GetSubList(JSThread *thread, const JSHandle &taggedList, + const int fromIndex, const int toIndex, const JSHandle &subList); + static JSHandle OwnKeys(JSThread *thread, const JSHandle &taggedList); + void Clear(const JSThread *thread); + bool IsEmpty() const; + bool Has(const JSTaggedValue &value); + JSTaggedValue Get(const int index); + int GetIndexOf(const JSTaggedValue &value); + int GetLastIndexOf(const JSTaggedValue &value); + void InsertNode(const JSThread *thread, const JSHandle &value, const int prevDataIndex, + const int finalDataIndex); + JSTaggedValue RemoveByIndex(JSThread *thread, const int &index); + JSTaggedValue Remove(JSThread *thread, const JSTaggedValue &element); + JSTaggedValue Equal(const JSHandle &compareList); + DECL_DUMP() +}; + +class TaggedDoubleList : public TaggedList { +public: + static const int ENTRY_SIZE = 3; + static TaggedDoubleList *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + + static JSTaggedValue Create(const JSThread *thread, int numberOfElements = TaggedDoubleList::DEFAULT_ARRAY_LENGHT); + static JSTaggedValue Add(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value); + static JSTaggedValue AddFirst(const JSThread *thread, const JSHandle &taggedList, + const JSHandle &value); + static JSTaggedValue Insert(JSThread *thread, const JSHandle &taggedList, + const JSHandle &value, const int index); + static JSTaggedValue Set(JSThread *thread, const JSHandle &taggedList, const int index, + const JSHandle &value); + static JSTaggedValue ConvertToArray(const JSThread *thread, const JSHandle &taggedList); + static JSHandle OwnKeys(JSThread *thread, const JSHandle &taggedList); + static JSTaggedValue RemoveFirstFound(JSThread *thread, const JSHandle &taggedList, + const JSTaggedValue &element); + static JSTaggedValue RemoveLastFound(JSThread *thread, const JSHandle &taggedList, + const JSTaggedValue &element); + void Clear(const JSThread *thread); + JSTaggedValue Get(const int index); + bool Has(const JSTaggedValue &value); + void InsertNode(const JSThread *thread, const JSHandle &value, const int prevDataIndex, + const int finalDataIndex); + JSTaggedValue RemoveFirst(JSThread *thread); + JSTaggedValue RemoveLast(JSThread *thread); + JSTaggedValue RemoveByIndex(JSThread *thread, const int &index); + JSTaggedValue Remove(JSThread *thread, const JSTaggedValue &element); + int GetIndexOf(const JSTaggedValue &value); + int GetLastIndexOf(const JSTaggedValue &value); + int FindPrevNodeByIndexAtLast(const int index) const; + int FindPrevNodeByValueAtLast(const JSTaggedValue &element); + DECL_DUMP() + +protected: + inline JSTaggedValue FindElementByIndexAtLast(int index) const; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_LIST_H diff --git a/ecmascript/tagged_tree.cpp b/ecmascript/tagged_tree.cpp index 6f590405..975a8fef 100644 --- a/ecmascript/tagged_tree.cpp +++ b/ecmascript/tagged_tree.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/tagged_tree.h b/ecmascript/tagged_tree.h index e86ffb00..5c2722a1 100644 --- a/ecmascript/tagged_tree.h +++ b/ecmascript/tagged_tree.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/tests/BUILD.gn b/ecmascript/tests/BUILD.gn index 415a0fa8..09327dd2 100644 --- a/ecmascript/tests/BUILD.gn +++ b/ecmascript/tests/BUILD.gn @@ -40,6 +40,8 @@ host_unittest_action("EcmaVmTest") { "js_api_arraylist_test.cpp", "js_api_deque_iterator_test.cpp", "js_api_deque_test.cpp", + "js_api_linked_list_test.cpp", + "js_api_list_test.cpp", "js_api_plain_array_test.cpp", "js_api_queue_test.cpp", "js_api_stack_iterator_test.cpp", diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index 83729a2a..504a88b2 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -33,6 +33,10 @@ #include "ecmascript/js_api_arraylist_iterator.h" #include "ecmascript/js_api_deque.h" #include "ecmascript/js_api_deque_iterator.h" +#include "ecmascript/js_api_linked_list.h" +#include "ecmascript/js_api_linked_list_iterator.h" +#include "ecmascript/js_api_list.h" +#include "ecmascript/js_api_list_iterator.h" #include "ecmascript/js_api_plain_array.h" #include "ecmascript/js_api_plain_array_iterator.h" #include "ecmascript/js_api_queue.h" @@ -95,6 +99,7 @@ #include "ecmascript/object_factory.h" #include "ecmascript/tagged_array.h" #include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_list.h" #include "ecmascript/tagged_tree.h" #include "ecmascript/template_map.h" #include "ecmascript/tests/test_helper.h" @@ -208,6 +213,28 @@ static JSHandle NewJSAPIPlainArray(JSThread *thread, ObjectFact return jSAPIPlainArray; } +static JSHandle NewJSAPIList(JSThread *thread, ObjectFactory *factory) +{ + auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proto = globalEnv->GetObjectFunctionPrototype(); + JSHandle listClass = factory->NewEcmaDynClass(JSAPIList::SIZE, JSType::JS_API_LIST, proto); + JSHandle jsAPIList = JSHandle::Cast(factory->NewJSObjectWithInit(listClass)); + JSHandle taggedSingleList(thread, TaggedSingleList::Create(thread)); + jsAPIList->SetSingleList(thread, taggedSingleList); + return jsAPIList; +} + +static JSHandle NewJSAPILinkedList(JSThread *thread, ObjectFactory *factory) +{ + auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proto = globalEnv->GetObjectFunctionPrototype(); + JSHandle mapClass = factory->NewEcmaDynClass(JSAPILinkedList::SIZE, JSType::JS_API_LINKED_LIST, proto); + JSHandle jsAPILinkedList = JSHandle::Cast(factory->NewJSObjectWithInit(mapClass)); + JSHandle linkedlist(thread, TaggedDoubleList::Create(thread)); + jsAPILinkedList->SetDoubleList(thread, linkedlist); + return jsAPILinkedList; +} + static JSHandle NewJSObject(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv) { JSFunction *jsFunc = globalEnv->GetObjectFunction().GetObject(); @@ -955,6 +982,37 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) DUMP_FOR_HANDLE(jsVectorIter) break; } + case JSType::JS_API_LIST: { + // 1 : 1 dump fileds number + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIList::SIZE, 1U) + JSHandle jsAPIList = NewJSAPIList(thread, factory); + DUMP_FOR_HANDLE(jsAPIList) + break; + } + case JSType::JS_API_LINKED_LIST: { + // 1 : 1 dump fileds number + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPILinkedList::SIZE, 1U) + JSHandle jsAPILinkedList = NewJSAPILinkedList(thread, factory); + DUMP_FOR_HANDLE(jsAPILinkedList) + break; + } + case JSType::JS_API_LIST_ITERATOR: { + // 2 : 2 dump fileds number + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIListIterator::SIZE, 2U) + JSHandle jsAPIList = NewJSAPIList(thread, factory); + JSHandle jsAPIListIter = factory->NewJSAPIListIterator(jsAPIList); + DUMP_FOR_HANDLE(jsAPIListIter) + break; + } + case JSType::JS_API_LINKED_LIST_ITERATOR: { + // 2 : 2 dump fileds number + CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIListIterator::SIZE, 2U) + JSHandle jsAPILinkedList = NewJSAPILinkedList(thread, factory); + JSHandle jsAPILinkedListIter = + factory->NewJSAPILinkedListIterator(jsAPILinkedList); + DUMP_FOR_HANDLE(jsAPILinkedListIter) + break; + } case JSType::MODULE_RECORD: { CHECK_DUMP_FIELDS(Record::SIZE, ModuleRecord::SIZE, 0U); break; diff --git a/ecmascript/tests/js_api_arraylist_test.cpp b/ecmascript/tests/js_api_arraylist_test.cpp index 709d9c45..4a7216f7 100644 --- a/ecmascript/tests/js_api_arraylist_test.cpp +++ b/ecmascript/tests/js_api_arraylist_test.cpp @@ -535,10 +535,9 @@ HWTEST_F_L0(JSAPIArrayListTest, GetOwnProperty) JSHandle value(thread, JSTaggedValue(i)); JSAPIArrayList::Add(thread, arrayList, value); } - PropertyDescriptor descRes(thread); for (uint32_t i = 0; i < elementsNums; i++) { JSHandle key(thread, JSTaggedValue(i)); - bool getOwnPropertyRes = JSAPIArrayList::GetOwnProperty(thread, arrayList, key, descRes); + bool getOwnPropertyRes = JSAPIArrayList::GetOwnProperty(thread, arrayList, key); EXPECT_EQ(getOwnPropertyRes, true); } } diff --git a/ecmascript/tests/js_api_deque_test.cpp b/ecmascript/tests/js_api_deque_test.cpp index 23fb5523..c7feb59b 100644 --- a/ecmascript/tests/js_api_deque_test.cpp +++ b/ecmascript/tests/js_api_deque_test.cpp @@ -167,4 +167,27 @@ HWTEST_F_L0(JSAPIDequeTest, PopLast) toor->Dump(); } + +HWTEST_F_L0(JSAPIDequeTest, GetOwnProperty) +{ + constexpr uint32_t DEFAULT_LENGTH = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + JSHandle toor(thread, CreateDeque()); + + std::string dequeValue("dequevalue"); + for (uint32_t i = 0; i < DEFAULT_LENGTH; i++) { + std::string ivalue = dequeValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIDeque::InsertFront(thread, toor, value); + JSAPIDeque::InsertEnd(thread, toor, value); + } + // test GetOwnProperty + int testInt = 1; + JSHandle dequeKey1(thread, JSTaggedValue(testInt)); + EXPECT_TRUE(JSAPIDeque::GetOwnProperty(thread, toor, dequeKey1)); + testInt = 20; + JSHandle dequeKey2(thread, JSTaggedValue(testInt)); + EXPECT_FALSE(JSAPIDeque::GetOwnProperty(thread, toor, dequeKey2)); +} } // namespace panda::test diff --git a/ecmascript/tests/js_api_linked_list_test.cpp b/ecmascript/tests/js_api_linked_list_test.cpp new file mode 100644 index 00000000..494689d8 --- /dev/null +++ b/ecmascript/tests/js_api_linked_list_test.cpp @@ -0,0 +1,338 @@ +/* + * 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_linked_list.h" +#include "ecmascript/js_api_linked_list_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/tagged_list.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +using namespace panda::ecmascript::containers; + +namespace panda::test { +class JSAPILinkedListTest : 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); + } + + EcmaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + +protected: + JSAPILinkedList *CreateLinkedList() + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromASCII("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + // 6 : 6 value is 6. + objCallInfo->SetCallArg(0, JSTaggedValue(6)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSHandle contianer = + JSHandle(thread, ContainersPrivate::Load(objCallInfo.get())); + JSHandle linkedList = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(contianer), + contianer)); + JSTaggedValue doubleList = TaggedDoubleList::Create(thread); + linkedList->SetDoubleList(thread, doubleList); + return *linkedList; + } +}; + +HWTEST_F_L0(JSAPILinkedListTest, LinkedListCreate) +{ + JSAPILinkedList *linkedlist = CreateLinkedList(); + EXPECT_TRUE(linkedlist != nullptr); +} + +HWTEST_F_L0(JSAPILinkedListTest, AddAndHas) +{ + constexpr int NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateLinkedList()); + + std::string myValue("myvalue"); + for (int i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPILinkedList::Add(thread, toor, value); + } + EXPECT_EQ(toor->Length(), NODE_NUMBERS); + + for (int i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } + JSTaggedValue gValue = toor->Get(10); + EXPECT_EQ(gValue, JSTaggedValue::Undefined()); + + std::string ivalue = myValue + std::to_string(1); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + EXPECT_TRUE(toor->Has(value.GetTaggedValue())); +} + +HWTEST_F_L0(JSAPILinkedListTest, AddFirstAndGetFirst) +{ + JSHandle value(thread, JSTaggedValue(1)); + JSHandle list(thread, CreateLinkedList()); + list->Add(thread, list, value); + EXPECT_EQ(list->Length(), 1); + EXPECT_EQ(list->Get(0).GetInt(), 1); + + JSHandle value1(thread, JSTaggedValue(2)); + list->AddFirst(thread, list, value1); + EXPECT_EQ(list->Length(), 2); + EXPECT_EQ(list->GetFirst().GetInt(), 2); +} + +HWTEST_F_L0(JSAPILinkedListTest, InsertAndGetLast) +{ // create jsMap + constexpr uint32_t NODE_NUMBERS = 9; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateLinkedList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSAPILinkedList::Add(thread, toor, value); + } + EXPECT_EQ(toor->GetLast().GetInt(), 9); + EXPECT_EQ(toor->GetFirst().GetInt(), 1); + + value.Update(JSTaggedValue(99)); + int len = toor->Length(); + toor->Insert(thread, toor, value, len); + EXPECT_EQ(toor->GetLast().GetInt(), 99); + EXPECT_EQ(toor->Length(), 10); + + value.Update(JSTaggedValue(100)); + toor->Insert(thread, toor, value, 0); + EXPECT_EQ(toor->GetFirst().GetInt(), 100); + EXPECT_EQ(toor->Length(), 11); + + toor->Dump(); + + value.Update(JSTaggedValue(101)); + toor->Insert(thread, toor, value, 5); + EXPECT_EQ(toor->Length(), 12); + toor->Dump(); + EXPECT_EQ(toor->Get(5).GetInt(), 101); +} + +HWTEST_F_L0(JSAPILinkedListTest, GetIndexOfAndGetLastIndexOf) +{ // create jsMap + constexpr uint32_t NODE_NUMBERS = 9; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateLinkedList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSAPILinkedList::Add(thread, toor, value); + } + EXPECT_EQ(toor->GetLast().GetInt(), 9); + EXPECT_EQ(toor->GetFirst().GetInt(), 1); + + value.Update(JSTaggedValue(99)); + int len = toor->Length(); + toor->Insert(thread, toor, value, len); + EXPECT_EQ(toor->GetIndexOf(value.GetTaggedValue()).GetInt(), 9); + EXPECT_EQ(toor->GetLastIndexOf(value.GetTaggedValue()).GetInt(), 9); + EXPECT_EQ(toor->Length(), 10); + + value.Update(JSTaggedValue(100)); + toor->Insert(thread, toor, value, 0); + EXPECT_EQ(toor->GetIndexOf(value.GetTaggedValue()).GetInt(), 0); + EXPECT_EQ(toor->GetLastIndexOf(value.GetTaggedValue()).GetInt(), 0); + EXPECT_EQ(toor->Length(), 11); + + value.Update(JSTaggedValue(101)); + toor->Insert(thread, toor, value, 5); + EXPECT_EQ(toor->GetIndexOf(value.GetTaggedValue()).GetInt(), 5); + EXPECT_EQ(toor->GetLastIndexOf(value.GetTaggedValue()).GetInt(), 5); + EXPECT_EQ(toor->Length(), 12); + + toor->Dump(); +} + +HWTEST_F_L0(JSAPILinkedListTest, Remove) +{ // create jsMap + constexpr uint32_t NODE_NUMBERS = 20; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateLinkedList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSAPILinkedList::Add(thread, toor, value); + } + EXPECT_EQ(toor->Length(), 20); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } + + EXPECT_EQ(JSAPILinkedList::RemoveFirst(thread, toor), JSTaggedValue(0)); + value.Update(JSTaggedValue(0)); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 19); + + EXPECT_EQ(JSAPILinkedList::RemoveLast(thread, toor), JSTaggedValue(19)); + value.Update(JSTaggedValue(19)); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 18); + + value.Update(JSTaggedValue(5)); + EXPECT_EQ(JSAPILinkedList::RemoveByIndex(thread, toor, 4), value.GetTaggedValue()); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 17); + + value.Update(JSTaggedValue(8)); + EXPECT_EQ(toor->Remove(thread, value.GetTaggedValue()), JSTaggedValue::True()); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 16); + + value.Update(JSTaggedValue(11)); + EXPECT_EQ(JSAPILinkedList::RemoveFirstFound(thread, toor, value.GetTaggedValue()), JSTaggedValue::True()); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 15); + + value.Update(JSTaggedValue(14)); + EXPECT_EQ(JSAPILinkedList::RemoveLastFound(thread, toor, value.GetTaggedValue()), JSTaggedValue::True()); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 14); + + toor->Dump(); +} + +HWTEST_F_L0(JSAPILinkedListTest, Clear) +{ + JSAPILinkedList *linkedlist = CreateLinkedList(); + + JSHandle value(thread, JSTaggedValue(1)); + JSHandle list(thread, linkedlist); + linkedlist->Add(thread, list, value); + + JSHandle value1(thread, JSTaggedValue(2)); + linkedlist->Insert(thread, list, value1, 0); + + linkedlist->Clear(thread); + + EXPECT_EQ(linkedlist->Length(), 0); + EXPECT_TRUE(linkedlist->GetFirst() == JSTaggedValue::Undefined()); +} + +HWTEST_F_L0(JSAPILinkedListTest, Set) +{ + constexpr uint32_t NODE_NUMBERS = 20; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateLinkedList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSAPILinkedList::Add(thread, toor, value); + } + EXPECT_EQ(toor->Length(), 20); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSAPILinkedList::Set(thread, toor, i, value); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } +} + +HWTEST_F_L0(JSAPILinkedListTest, GetOwnProperty) +{ + constexpr uint32_t DEFAULT_LENGTH = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + JSHandle toor(thread, CreateLinkedList()); + + std::string linkedListvalue("linkedListvalue"); + for (uint32_t i = 0; i < DEFAULT_LENGTH; i++) { + std::string ivalue = linkedListvalue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPILinkedList::Add(thread, toor, value); + } + // test GetOwnProperty + int testInt = 1; + JSHandle linkedListKey1(thread, JSTaggedValue(testInt)); + EXPECT_TRUE(JSAPILinkedList::GetOwnProperty(thread, toor, linkedListKey1)); + testInt = 20; + JSHandle linkedListKey2(thread, JSTaggedValue(testInt)); + EXPECT_FALSE(JSAPILinkedList::GetOwnProperty(thread, toor, linkedListKey2)); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_api_list_test.cpp b/ecmascript/tests/js_api_list_test.cpp new file mode 100644 index 00000000..b1ba9523 --- /dev/null +++ b/ecmascript/tests/js_api_list_test.cpp @@ -0,0 +1,303 @@ +/* + * 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_list.h" +#include "ecmascript/js_api_list_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/tagged_list.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +using namespace panda::ecmascript::containers; + +namespace panda::test { +class JSAPIListTest : 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); + } + + EcmaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + +protected: + JSAPIList *CreateList() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle globalObject = env->GetJSGlobalObject(); + JSHandle key(factory->NewFromASCII("ArkPrivate")); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(value.GetTaggedValue()); + objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(containers::ContainerTag::List))); + + [[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 list(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSTaggedValue singleList = TaggedSingleList::Create(thread); + list->SetSingleList(thread, singleList); + return *list; + } +}; + +HWTEST_F_L0(JSAPIListTest, listCreate) +{ + JSAPIList *list = CreateList(); + EXPECT_TRUE(list != nullptr); +} + +HWTEST_F_L0(JSAPIListTest, AddAndHas) +{ + constexpr int NODE_NUMBERS = 9; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateList()); + + std::string myValue("myvalue"); + for (int i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIList::Add(thread, toor, value); + } + EXPECT_EQ(toor->Length(), NODE_NUMBERS); + + for (int i = 0; i < NODE_NUMBERS; i++) { + std::string ivalue = myValue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } + JSTaggedValue gValue = toor->Get(10); + EXPECT_EQ(gValue, JSTaggedValue::Undefined()); + + std::string ivalue = myValue + std::to_string(1); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + EXPECT_TRUE(toor->Has(value.GetTaggedValue())); + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIListTest, InsertAndGetLastAndGetFirst) +{ // create jsMap + constexpr uint32_t NODE_NUMBERS = 9; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSAPIList::Add(thread, toor, value); + } + EXPECT_EQ(toor->GetLast().GetInt(), 9); + EXPECT_EQ(toor->GetFirst().GetInt(), 1); + + value.Update(JSTaggedValue(99)); + int len = toor->Length(); + toor->Insert(thread, toor, value, len); + EXPECT_EQ(toor->GetLast().GetInt(), 99); + EXPECT_EQ(toor->Length(), 10); + + value.Update(JSTaggedValue(100)); + toor->Insert(thread, toor, value, 0); + EXPECT_EQ(toor->GetFirst().GetInt(), 100); + EXPECT_EQ(toor->Length(), 11); + + toor->Dump(); + + value.Update(JSTaggedValue(101)); + toor->Insert(thread, toor, value, 5); + EXPECT_EQ(toor->Length(), 12); + toor->Dump(); + EXPECT_EQ(toor->Get(5).GetInt(), 101); +} + +HWTEST_F_L0(JSAPIListTest, GetIndexOfAndGetLastIndexOf) +{ // create jsMap + constexpr uint32_t NODE_NUMBERS = 9; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSAPIList::Add(thread, toor, value); + } + EXPECT_EQ(toor->GetLast().GetInt(), 9); + EXPECT_EQ(toor->GetFirst().GetInt(), 1); + + value.Update(JSTaggedValue(99)); + int len = toor->Length(); + toor->Insert(thread, toor, value, len); + EXPECT_EQ(toor->GetIndexOf(value.GetTaggedValue()).GetInt(), 9); + EXPECT_EQ(toor->GetLastIndexOf(value.GetTaggedValue()).GetInt(), 9); + EXPECT_EQ(toor->Length(), 10); + + value.Update(JSTaggedValue(100)); + toor->Insert(thread, toor, value, 0); + EXPECT_EQ(toor->GetIndexOf(value.GetTaggedValue()).GetInt(), 0); + EXPECT_EQ(toor->GetLastIndexOf(value.GetTaggedValue()).GetInt(), 0); + EXPECT_EQ(toor->Length(), 11); + + value.Update(JSTaggedValue(101)); + toor->Insert(thread, toor, value, 5); + EXPECT_EQ(toor->GetIndexOf(value.GetTaggedValue()).GetInt(), 5); + EXPECT_EQ(toor->GetLastIndexOf(value.GetTaggedValue()).GetInt(), 5); + EXPECT_EQ(toor->Length(), 12); + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIListTest, Remove) +{ // create jsMap + constexpr uint32_t NODE_NUMBERS = 20; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSAPIList::Add(thread, toor, value); + } + EXPECT_EQ(toor->Length(), 20); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } + + value.Update(JSTaggedValue(4)); + EXPECT_EQ(JSAPIList::RemoveByIndex(thread, toor, 4), value.GetTaggedValue()); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 19); + + value.Update(JSTaggedValue(8)); + EXPECT_EQ(toor->Remove(thread, value.GetTaggedValue()), JSTaggedValue::True()); + EXPECT_EQ(toor->Has(value.GetTaggedValue()), false); + EXPECT_EQ(toor->Length(), 18); + + toor->Dump(); +} + +HWTEST_F_L0(JSAPIListTest, Clear) +{ + JSHandle value(thread, JSTaggedValue(1)); + JSHandle list(thread, CreateList()); + list->Add(thread, list, value); + + JSHandle value1(thread, JSTaggedValue(2)); + list->Insert(thread, list, value1, 0); + + list->Clear(thread); + + EXPECT_EQ(list->Length(), 0); + EXPECT_TRUE(list->GetFirst() == JSTaggedValue::Undefined()); +} + +HWTEST_F_L0(JSAPIListTest, Set) +{ + constexpr uint32_t NODE_NUMBERS = 20; + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + JSHandle toor(thread, CreateList()); + EXPECT_EQ(toor->GetLast(), JSTaggedValue::Undefined()); + EXPECT_EQ(toor->GetFirst(), JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSAPIList::Add(thread, toor, value); + } + EXPECT_EQ(toor->Length(), 20); + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i)); + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSAPIList::Set(thread, toor, i, value); + } + + for (uint32_t i = 0; i < NODE_NUMBERS; i++) { + value.Update(JSTaggedValue(i + 1)); + JSTaggedValue gValue = toor->Get(i); + EXPECT_EQ(gValue, value.GetTaggedValue()); + } +} + +HWTEST_F_L0(JSAPIListTest, GetOwnProperty) +{ + constexpr uint32_t DEFAULT_LENGTH = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + JSHandle toor(thread, CreateList()); + + std::string listvalue("listvalue"); + for (uint32_t i = 0; i < DEFAULT_LENGTH; i++) { + std::string ivalue = listvalue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIList::Add(thread, toor, value); + } + // test GetOwnProperty + int testInt = 1; + JSHandle listKey1(thread, JSTaggedValue(testInt)); + EXPECT_TRUE(JSAPIList::GetOwnProperty(thread, toor, listKey1)); + testInt = 20; + JSHandle listKey2(thread, JSTaggedValue(testInt)); + EXPECT_FALSE(JSAPIList::GetOwnProperty(thread, toor, listKey2)); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_api_plain_array_test.cpp b/ecmascript/tests/js_api_plain_array_test.cpp index e8b7b73b..430ad64b 100644 --- a/ecmascript/tests/js_api_plain_array_test.cpp +++ b/ecmascript/tests/js_api_plain_array_test.cpp @@ -250,4 +250,29 @@ HWTEST_F_L0(JSAPIPlainArrayTest, PA_RemvoeAnrRemvoeAtAndSetValueAtAndGetValueAt) taggedValue = array->GetValueAt(lvalue); EXPECT_TRUE(JSTaggedValue::Equal(thread, value, JSHandle(thread, taggedValue))); } + +HWTEST_F_L0(JSAPIPlainArrayTest, GetOwnProperty) +{ + constexpr uint32_t DEFAULT_LENGTH = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle toor(thread, CreatePlainArray()); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + + std::string plainArrayvalue("plainArrayvalue"); + for (uint32_t i = 0; i < DEFAULT_LENGTH; i++) { + uint32_t ikey = 100 + i; + std::string ivalue = plainArrayvalue + std::to_string(i); + key.Update(JSTaggedValue(ikey)); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIPlainArray::Add(thread, toor, key, value); + } + // test GetOwnProperty + int testInt = 100 + 1; + JSHandle plainArrayKey1(thread, JSTaggedValue(testInt)); + EXPECT_TRUE(JSAPIPlainArray::GetOwnProperty(thread, toor, plainArrayKey1)); + testInt = 100 + 20; + JSHandle plainArrayKey2(thread, JSTaggedValue(testInt)); + EXPECT_FALSE(JSAPIPlainArray::GetOwnProperty(thread, toor, plainArrayKey2)); +} } // namespace panda::test diff --git a/ecmascript/tests/js_api_queue_test.cpp b/ecmascript/tests/js_api_queue_test.cpp index ebb4a228..bd4ed93b 100644 --- a/ecmascript/tests/js_api_queue_test.cpp +++ b/ecmascript/tests/js_api_queue_test.cpp @@ -188,7 +188,6 @@ HWTEST_F_L0(JSAPIQueueTest, GetOwnProperty) ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSMutableHandle value(thread, JSTaggedValue::Undefined()); JSHandle jsQueue = CreateQueue(); - PropertyDescriptor queueDesc(thread); std::string queueValue("queuevalue"); for (uint32_t i = 0; i < DEFAULT_LENGTH; i++) { @@ -199,9 +198,9 @@ HWTEST_F_L0(JSAPIQueueTest, GetOwnProperty) // test GetOwnProperty int testInt = 1; JSHandle queueKey1(thread, JSTaggedValue(testInt)); - EXPECT_TRUE(JSAPIQueue::GetOwnProperty(thread, jsQueue, queueKey1, queueDesc)); + EXPECT_TRUE(JSAPIQueue::GetOwnProperty(thread, jsQueue, queueKey1)); testInt = 9; JSHandle queueKey2(thread, JSTaggedValue(testInt)); - EXPECT_FALSE(JSAPIQueue::GetOwnProperty(thread, jsQueue, queueKey2, queueDesc)); + EXPECT_FALSE(JSAPIQueue::GetOwnProperty(thread, jsQueue, queueKey2)); } } // namespace panda::test \ No newline at end of file diff --git a/ecmascript/tests/js_api_stack_test.cpp b/ecmascript/tests/js_api_stack_test.cpp index e4ab3371..b3adb6f4 100644 --- a/ecmascript/tests/js_api_stack_test.cpp +++ b/ecmascript/tests/js_api_stack_test.cpp @@ -191,4 +191,26 @@ HWTEST_F_L0(JSAPIStackTest, Search) toor->Dump(); } + +HWTEST_F_L0(JSAPIStackTest, GetOwnProperty) +{ + constexpr uint32_t DEFAULT_LENGTH = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + JSHandle toor(thread, CreateStack()); + + std::string stackvalue("stackvalue"); + for (uint32_t i = 0; i < DEFAULT_LENGTH; i++) { + std::string ivalue = stackvalue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIStack::Push(thread, toor, value); + } + // test GetOwnProperty + int testInt = 1; + JSHandle stackKey1(thread, JSTaggedValue(testInt)); + EXPECT_TRUE(JSAPIStack::GetOwnProperty(thread, toor, stackKey1)); + testInt = 20; + JSHandle stackKey2(thread, JSTaggedValue(testInt)); + EXPECT_FALSE(JSAPIStack::GetOwnProperty(thread, toor, stackKey2)); +} } // namespace panda::test diff --git a/ecmascript/tests/js_api_tree_map_test.cpp b/ecmascript/tests/js_api_tree_map_test.cpp index d829b2ea..7e869d32 100644 --- a/ecmascript/tests/js_api_tree_map_test.cpp +++ b/ecmascript/tests/js_api_tree_map_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/tests/js_api_tree_set_test.cpp b/ecmascript/tests/js_api_tree_set_test.cpp index 5daaf572..843ee9d5 100644 --- a/ecmascript/tests/js_api_tree_set_test.cpp +++ b/ecmascript/tests/js_api_tree_set_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/ecmascript/tests/js_api_vector_test.cpp b/ecmascript/tests/js_api_vector_test.cpp index 544cb806..08499bcc 100644 --- a/ecmascript/tests/js_api_vector_test.cpp +++ b/ecmascript/tests/js_api_vector_test.cpp @@ -184,4 +184,26 @@ HWTEST_F_L0(JSAPIVectorTest, GetIndexOf) toor->Dump(); } + +HWTEST_F_L0(JSAPIVectorTest, GetOwnProperty) +{ + constexpr uint32_t DEFAULT_LENGTH = 8; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + JSHandle toor(thread, CreateVector()); + + std::string vectorvalue("vectorvalue"); + for (uint32_t i = 0; i < DEFAULT_LENGTH; i++) { + std::string ivalue = vectorvalue + std::to_string(i); + value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); + JSAPIVector::Add(thread, toor, value); + } + // test GetOwnProperty + int testInt = 1; + JSHandle vectorKey1(thread, JSTaggedValue(testInt)); + EXPECT_TRUE(JSAPIVector::GetOwnProperty(thread, toor, vectorKey1)); + testInt = 20; + JSHandle vectorKey2(thread, JSTaggedValue(testInt)); + EXPECT_FALSE(JSAPIVector::GetOwnProperty(thread, toor, vectorKey2)); +} } // namespace panda::test diff --git a/ecmascript/tests/tagged_tree_test.cpp b/ecmascript/tests/tagged_tree_test.cpp index e07bf934..ae5f7ac0 100644 --- a/ecmascript/tests/tagged_tree_test.cpp +++ b/ecmascript/tests/tagged_tree_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/test/moduletest/container/BUILD.gn b/test/moduletest/container/BUILD.gn index 37f457bd..28f40f2e 100644 --- a/test/moduletest/container/BUILD.gn +++ b/test/moduletest/container/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Huawei Device Co., Ltd. +# 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 @@ -29,15 +29,27 @@ host_moduletest_action("container_vector") { deps = [] } +host_moduletest_action("container_list") { + deps = [] +} + +host_moduletest_action("container_linked_list") { + deps = [] +} + host_moduletest_action("container") { extra_modules = [ "container_arraylist", + "container_linked_list", + "container_list", "container_treemap", "container_treeset", "container_vector", ] deps = [ ":gen_container_arraylist_abc", + ":gen_container_linked_list_abc", + ":gen_container_list_abc", ":gen_container_treemap_abc", ":gen_container_treeset_abc", ":gen_container_vector_abc", diff --git a/test/moduletest/container/container.js b/test/moduletest/container/container.js index 0de6186f..219bc974 100644 --- a/test/moduletest/container/container.js +++ b/test/moduletest/container/container.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/test/moduletest/container/container_arraylist.js b/test/moduletest/container/container_arraylist.js index 76e41970..a9a18c7e 100644 --- a/test/moduletest/container/container_arraylist.js +++ b/test/moduletest/container/container_arraylist.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/test/moduletest/container/container_linked_list.js b/test/moduletest/container/container_linked_list.js new file mode 100644 index 00000000..21945d0d --- /dev/null +++ b/test/moduletest/container/container_linked_list.js @@ -0,0 +1,130 @@ +/* + * 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. + */ + +var LinkedList = undefined; +if (globalThis["ArkPrivate"] != undefined) { + LinkedList = ArkPrivate.Load(ArkPrivate.LinkedList); + let list = new LinkedList(); + let testArray = [] + for(let i = 0; i<10; i++) { + list.add(i) + testArray.push(i) + } + print("test linkedlist has:", list.has(8)) + print("test linkedlist not has:", list.has(2)) + print("test linkedlist getLastIndexOf:", list.getLastIndexOf(1) === 1) + print("test linkedlist getIndexOf:", list.getIndexOf(5) === 5) + + list.removeByIndex(9) + + testArray.splice(9, 1) + let res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test linkedlist removeByIndex:", res) + + const removeRes = list.remove(8) + testArray.splice(8, 1) + res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test linkedlist remove:", res) + print("test linkedlist remove1:", removeRes) + print("test linkedlist getFirst:", list.getFirst() === 0) + print("test linkedlist getLast:", list.getLast() === 7) + + list.insert(3, 999) + testArray.splice(3, 0, 999) + res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test linkedlist insert:", res) + + list.set(5, 888) + testArray[5] = 888 + res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test linkedlist set:", res) + + print("test linkedlist clone:", res) + + list.addFirst(1111) + print("test linkedlist addfirst:", list.getFirst() === 1111) + + const removefirstres = list.removeFirst() + print("test linkedlist removeFirst:", removefirstres === 1111) + + res = true + let i = 0 + for (const data of list) { + if (data !== testArray[i]) { + res = false + } + i++; + } + print("test linkedlist intertor:", res) + + let list1 = new LinkedList(); + let testArray1 = [] + for (let i = 0; i < 10; i++) { + list1.add(i) + testArray1.push(i) + } + + res = true + list1.forEach((i, d) => { + if (d !== testArray1[i]) { + res = false + } + }) + + print("test linkedlist forEach:", res) + list1.clear() + print("test linkedlist clear:", list1.length === 0) + print("test linkedlist get:", list.get(1232) === undefined) + print("test linkedlist getLastIndexOf:", list.getLastIndexOf('abc') === -1) + try { + list.removeByIndex(99) + } catch (error) { + print("test linkedlist removeByIndex:", 'There is no such element to delete') + } + + testArray.splice(5, 1) + const resRemove = list.remove(888) + print("test linkedlist remove:", resRemove) + + res = true + const arr = list.convertToArray() + for (let i = 1; i < arr.length; i++) { + if (arr[i] !== testArray[i]) { + res = false + } + } + print("test linkedlist convertToArray:", res) +} + diff --git a/test/moduletest/container/container_list.js b/test/moduletest/container/container_list.js new file mode 100644 index 00000000..4e678c07 --- /dev/null +++ b/test/moduletest/container/container_list.js @@ -0,0 +1,160 @@ +/* + * 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. + */ + +var List = undefined; +if (globalThis["ArkPrivate"] != undefined) { + List = ArkPrivate.Load(ArkPrivate.List); + let list = new List(); + const testArray = [] + for(let i = 0; i < 10; i++) { + list.add(i) + testArray.push(i) + } + + print("test list get 1:", list.get(1) === 1) + print("test list has:", list.has(8)) + print("test list not has:", list.has(123) === false) + + let list1 = new List(); + const testArray2 = [] + for(let i = 0; i < 10; i++) { + list1.add(i) + testArray2.push(i) + } + + print("test list equal:", list.equal(list1)) + list.add(10) + testArray.push(10) + print("test list equal:", list.equal(list1) === false) + print("test list getLastIndexOf:", list.getLastIndexOf(1) === 1) + print("test list getIndexOf:", list.getIndexOf(5) === 5) + + list.removeByIndex(10) + testArray.splice(10, 1) + let res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test list removeByIndex:", res) + + list.remove(9) + testArray.splice(9, 1) + res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + testArray[i] = testArray[i] * 2 + } + print("test list remove:", res) + + list.replaceAllElements((item, index) => { + return item * 2 + }) + res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test list replaceAllElements:", res) + print("test list getFirst:", list.getFirst() === 0) + print("test list getLast:", list.getLast() === 16) + list.insert(999, 3) + testArray.splice(3, 0, 999) + res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test list insert:", res) + + list.set(5, 888) + testArray[5] = 888 + res = true + for(let i = 0; i < testArray.length; i++) { + if (list[i] !== testArray[i]) { + res = false + } + } + print("test list set:", res) + + let list2 = new List(); + list2.add(4); + list2.add(3); + list2.add(1); + list2.add(2); + list2.add(0); + list2.sort((a,b) => a-b); + res = true + for (let i = 0; i < 5; i++) { + if (list2[i] !== i) { + res = false + } + } + print("test list sort:", res) + + res = true + let subList = list.getSubList(1, 3) + const newtestArray = testArray.slice(1, 3) + for(let i = 0; i < subList.length; i++) { + if (newtestArray[i] !== subList[i]) { + res = false + } + } + print("test list getSubList:", res) + + res = true + const arr = list.convertToArray() + for (let i = 0; i < arr.length; i++) { + if (arr[i] !== testArray[i]) { + res = false + } + } + print("test list convertToArray:", res) + + res = true + let i = 0 + for (const data of list) { + if (data !== testArray[i]) { + res = false + } + i++; + } + print("test list itertor:", res) + + res = true + list1.forEach((i, d) => { + if (d !== testArray2[i]) { + res = false + } + }) + print("test list forEach:", res) + list2.clear() + print("test list clear:", list2.length === 0) + print("test list get:", list1.get(200) === undefined) + print("test list getLastIndexOf:", list1.getLastIndexOf('abc') === -1) + try { + list1.removeByIndex(99) + } catch (error) { + print("test list removeByIndex:", 'There is no such element to delete') + } + res = list1.remove(888) + print("test list remove:", res) +} + diff --git a/test/moduletest/container/container_treemap.js b/test/moduletest/container/container_treemap.js index 77d2e94f..2e64b2e8 100644 --- a/test/moduletest/container/container_treemap.js +++ b/test/moduletest/container/container_treemap.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/test/moduletest/container/container_treeset.js b/test/moduletest/container/container_treeset.js index 4dfdd1d3..94123d08 100644 --- a/test/moduletest/container/container_treeset.js +++ b/test/moduletest/container/container_treeset.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 diff --git a/test/moduletest/container/expect_output.txt b/test/moduletest/container/expect_output.txt index 16e92e5b..a7a47b12 100644 --- a/test/moduletest/container/expect_output.txt +++ b/test/moduletest/container/expect_output.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Huawei Device Co., Ltd. +# 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 @@ -14,6 +14,52 @@ container test start 1 TypeError: Cannot set property on Container +test linkedlist has: true +test linkedlist not has: true +test linkedlist getLastIndexOf: true +test linkedlist getIndexOf: true +test linkedlist removeByIndex: true +test linkedlist remove: true +test linkedlist remove1: true +test linkedlist getFirst: true +test linkedlist getLast: true +test linkedlist insert: true +test linkedlist set: true +test linkedlist clone: true +test linkedlist addfirst: true +test linkedlist removeFirst: true +test linkedlist intertor: true +test linkedlist forEach: true +test linkedlist clear: true +test linkedlist get: true +test linkedlist getLastIndexOf: true +test linkedlist removeByIndex: There is no such element to delete +test linkedlist remove: true +test linkedlist convertToArray: true +test list get 1: true +test list has: true +test list not has: true +test list equal: true +test list equal: true +test list getLastIndexOf: true +test list getIndexOf: true +test list removeByIndex: true +test list remove: true +test list replaceAllElements: true +test list getFirst: true +test list getLast: true +test list insert: true +test list set: true +test list sort: true +test list getSubList: true +test list convertToArray: true +test list itertor: true +test list forEach: true +test list clear: true +test list get: true +test list getLastIndexOf: true +test list removeByIndex: There is no such element to delete +test list remove: false ### test TreeMap start ### test get, out: true test hasKey and hasValue, out: true