diff --git a/BUILD.gn b/BUILD.gn index 4889a723..04a8dc3d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -319,6 +319,7 @@ ecma_source = [ "ecmascript/class_linker/panda_file_translator.cpp", "ecmascript/containers/containers_arraylist.cpp", "ecmascript/containers/containers_private.cpp", + "ecmascript/containers/containers_queue.cpp", "ecmascript/containers/containers_treemap.cpp", "ecmascript/containers/containers_treeset.cpp", "ecmascript/dfx/vmstat/caller_stat.cpp", @@ -350,6 +351,8 @@ ecma_source = [ "ecmascript/jobs/micro_job_queue.cpp", "ecmascript/jspandafile/js_pandafile.cpp", "ecmascript/jspandafile/js_pandafile_manager.cpp", + "ecmascript/js_api_queue.cpp", + "ecmascript/js_api_queue_iterator.cpp", "ecmascript/js_api_tree_map.cpp", "ecmascript/js_api_tree_map_iterator.cpp", "ecmascript/js_api_tree_set.cpp", diff --git a/ecmascript/builtins.cpp b/ecmascript/builtins.cpp index bacfc553..ca7aecfe 100644 --- a/ecmascript/builtins.cpp +++ b/ecmascript/builtins.cpp @@ -65,6 +65,7 @@ #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/containers/containers_private.h" #include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_api_queue.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraybuffer.h" #include "ecmascript/js_api_arraylist.h" diff --git a/ecmascript/compiler/stub.cpp b/ecmascript/compiler/stub.cpp index 6f48a05a..ab225ac1 100644 --- a/ecmascript/compiler/stub.cpp +++ b/ecmascript/compiler/stub.cpp @@ -2950,7 +2950,7 @@ GateRef Stub::GetContainerProperty(GateRef glue, GateRef receiver, GateRef index }; std::array keyValues = { // 2 : 2 means that there are 2 args in total. static_cast(JSType::JS_API_ARRAY_LIST), - static_cast(JSType::JS_QUEUE), + static_cast(JSType::JS_API_QUEUE), }; // 2 : 2 means that there are 2 cases. Switch(ZExtInt32ToInt64(jsType), &defaultLabel, keyValues.data(), repCaseLabels.data(), 2); diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp index b9b08eb7..8b84faca 100644 --- a/ecmascript/containers/containers_private.cpp +++ b/ecmascript/containers/containers_private.cpp @@ -16,11 +16,14 @@ #include "ecmascript/containers/containers_private.h" #include "containers_arraylist.h" +#include "containers_queue.h" #include "containers_treemap.h" #include "containers_treeset.h" #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.h" #include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_api_queue.h" +#include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_tree_map.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set.h" @@ -57,7 +60,10 @@ JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) res = InitializeContainer(thread, thisValue, InitializeTreeSet, "TreeSetConstructor"); break; } - case ContainerTag::Queue: + case ContainerTag::Queue: { + res = InitializeContainer(thread, thisValue, InitializeQueue, "QueueConstructor"); + break; + } case ContainerTag::Deque: case ContainerTag::Stack: case ContainerTag::Vector: @@ -414,4 +420,57 @@ void ContainersPrivate::InitializeTreeSetIterator(JSThread *thread) auto globalConst = const_cast(thread->GlobalConstants()); globalConst->SetConstant(ConstantIndex::TREESET_ITERATOR_PROTOTYPE_INDEX, setIteratorPrototype.GetTaggedValue()); } + +JSHandle ContainersPrivate::InitializeQueue(JSThread *thread) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Queue.prototype + JSHandle queueFuncPrototype = factory->NewEmptyJSObject(); + JSHandle queueFuncPrototypeValue(queueFuncPrototype); + // Queue.prototype_or_dynclass + JSHandle queueInstanceDynclass = + factory->NewEcmaDynClass(JSAPIQueue::SIZE, JSType::JS_API_QUEUE, queueFuncPrototypeValue); + // Queue() = new Function() + JSHandle queueFunction(NewContainerConstructor( + thread, queueFuncPrototype, ContainersQueue::QueueConstructor, "Queue", FuncLength::ZERO)); + JSHandle(queueFunction)->SetFunctionPrototype(thread, queueInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(queueFuncPrototype), constructorKey, queueFunction); + + // Queue.prototype.add() + SetFrozenFunction(thread, queueFuncPrototype, "add", ContainersQueue::Add, FuncLength::ONE); + SetFrozenFunction(thread, queueFuncPrototype, "getFirst", ContainersQueue::GetFirst, FuncLength::ZERO); + SetFrozenFunction(thread, queueFuncPrototype, "pop", ContainersQueue::Pop, FuncLength::ZERO); + SetFrozenFunction(thread, queueFuncPrototype, "forEach", ContainersQueue::ForEach, FuncLength::TWO); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + SetStringTagSymbol(thread, env, queueFuncPrototype, "Queue"); + + JSHandle lengthGetter = CreateGetter(thread, ContainersQueue::GetSize, "length", FuncLength::ZERO); + JSHandle lengthKey(thread, globalConst->GetLengthString()); + SetGetter(thread, queueFuncPrototype, lengthKey, lengthGetter); + + SetFunctionAtSymbol(thread, env, queueFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + ContainersQueue::GetIteratorObj, FuncLength::ONE); + + ContainersPrivate::InitializeQueueIterator(thread, env, globalConst); + return queueFunction; +} + +void ContainersPrivate::InitializeQueueIterator(JSThread *thread, const JSHandle &env, + GlobalEnvConstants *globalConst) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iteratorFuncDynclass = + factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ITERATOR, env->GetIteratorPrototype()); + // QueueIterator.prototype + JSHandle queueIteratorPrototype(factory->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFrozenFunction(thread, queueIteratorPrototype, "next", JSAPIQueueIterator::Next, FuncLength::ONE); + SetStringTagSymbol(thread, env, queueIteratorPrototype, "Queue Iterator"); + globalConst->SetConstant(ConstantIndex::QUEUE_ITERATOR_PROTOTYPE_INDEX, queueIteratorPrototype.GetTaggedValue()); +} } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_private.h b/ecmascript/containers/containers_private.h index 3ed391fc..0a3b08c0 100644 --- a/ecmascript/containers/containers_private.h +++ b/ecmascript/containers/containers_private.h @@ -74,6 +74,9 @@ private: static void InitializeTreeMapIterator(JSThread *thread); static JSHandle InitializeTreeSet(JSThread *thread); static void InitializeTreeSetIterator(JSThread *thread); + static JSHandle InitializeQueue(JSThread *thread); + static void InitializeQueueIterator(JSThread *thread, const JSHandle &env, + GlobalEnvConstants *globalConst); }; } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_queue.cpp b/ecmascript/containers/containers_queue.cpp new file mode 100644 index 00000000..642b3f30 --- /dev/null +++ b/ecmascript/containers/containers_queue.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "containers_queue.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/internal_call_params.h" +#include "ecmascript/js_api_queue.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersQueue::QueueConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Queue, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newTaggedArray = factory->NewTaggedArray(JSAPIQueue::DEFAULT_CAPACITY_LENGTH); + obj->SetElements(thread, newTaggedArray); + + return obj.GetTaggedValue(); +} + +JSTaggedValue ContainersQueue::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIQueue()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIQueue", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 0); + JSAPIQueue::Add(thread, JSHandle::Cast(self), value); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersQueue::GetFirst(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIQueue()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIQueue", JSTaggedValue::Exception()); + } + + JSTaggedValue value = JSAPIQueue::GetFirst(thread, JSHandle::Cast(self)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + return value; +} + +JSTaggedValue ContainersQueue::Pop(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIQueue()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIQueue", JSTaggedValue::Exception()); + } + + JSTaggedValue value = JSAPIQueue::Pop(thread, JSHandle::Cast(self)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + return value; +} + +JSTaggedValue ContainersQueue::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); // JSAPIQueue + if (!thisHandle->IsJSAPIQueue()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIQueue", JSTaggedValue::Exception()); + } + + JSHandle queue = JSHandle::Cast(thisHandle); + // Let len be ToLength(Get(O, "length")). + double len = JSAPIQueue::GetArrayLength(thread, queue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle callbackFnHandle = GetCallArg(argv, 0); + // If IsCallable(callbackfn) is false, throw a TypeError exception. + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + // If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + + uint32_t index = queue->GetCurrentFront(); + uint32_t k = 0; + while (k < len) { + JSHandle kValue = + JSHandle(thread, queue->Get(thread, index)); + index = queue->GetNextPosition(index); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisHandle); + JSTaggedValue funcResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + k++; + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ContainersQueue::GetIteratorObj(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Queue, GetIteratorObj); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle self = GetThis(argv); + if (!self->IsJSAPIQueue()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIQueue", JSTaggedValue::Exception()); + } + JSHandle iter(factory->NewJSAPIQueueIterator(JSHandle::Cast(self))); + return iter.GetTaggedValue(); +} + +JSTaggedValue ContainersQueue::GetSize(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Queue, GetSize); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSAPIQueue()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIQueue", JSTaggedValue::Exception()); + } + + uint32_t length = JSHandle::Cast(self)->GetSize(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return JSTaggedValue(length); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_queue.h b/ecmascript/containers/containers_queue.h new file mode 100644 index 00000000..3cd84dd6 --- /dev/null +++ b/ecmascript/containers/containers_queue.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_QUEUE_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_QUEUE_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +class ContainersQueue : public base::BuiltinsBase { +public: + static JSTaggedValue QueueConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetFirst(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Pop(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetIteratorObj(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_QUEUE_H diff --git a/ecmascript/dfx/hprof/heap_snapshot.cpp b/ecmascript/dfx/hprof/heap_snapshot.cpp index faa273a7..d95407f9 100644 --- a/ecmascript/dfx/hprof/heap_snapshot.cpp +++ b/ecmascript/dfx/hprof/heap_snapshot.cpp @@ -334,6 +334,10 @@ CString *HeapSnapShot::GenerateNodeName(JSThread *thread, TaggedObject *entry) return GetString("TreeMapIterator"); case JSType::JS_API_TREESET_ITERATOR: return GetString("TreeSetIterator"); + case JSType::JS_API_QUEUE: + return GetString("Queue"); + case JSType::JS_API_QUEUE_ITERATOR: + return GetString("QueueIterator"); default: break; } diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index 6d796b90..201684e2 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -31,6 +31,8 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_queue.h" +#include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_tree_map.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set.h" @@ -282,6 +284,10 @@ CString JSHClass::DumpJSType(JSType type) return "TreeMapIterator"; case JSType::JS_API_TREESET_ITERATOR: return "TreeSetIterator"; + case JSType::JS_API_QUEUE: + return "Queue"; + case JSType::JS_API_QUEUE_ITERATOR: + return "QueueIterator"; default: { CString ret = "unknown type "; return ret + static_cast(type); @@ -655,6 +661,12 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, std::ostream &os) case JSType::JS_API_TREESET_ITERATOR: JSAPITreeSetIterator::Cast(obj)->Dump(thread, os); break; + case JSType::JS_API_QUEUE: + JSAPIQueue::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_API_QUEUE_ITERATOR: + JSAPIQueueIterator::Cast(obj)->Dump(thread, os); + break; default: UNREACHABLE(); break; @@ -1361,6 +1373,22 @@ void JSArrayIterator::Dump(JSThread *thread, std::ostream &os) const JSObject::Dump(thread, os); } +void JSAPIQueue::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - length: " << std::dec << GetSize() << "\n"; + os << " - front: " << std::dec << GetFront() << "\n"; + os << " - tail: " << std::dec << GetTail() << "\n"; + JSObject::Dump(thread, os); +} + +void JSAPIQueueIterator::Dump(JSThread *thread, std::ostream &os) const +{ + JSAPIQueue *queue = JSAPIQueue::Cast(GetIteratedQueue().GetTaggedObject()); + os << " - length: " << std::dec << queue->GetSize() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex() << "\n"; + JSObject::Dump(thread, os); +} + void JSStringIterator::Dump(JSThread *thread, std::ostream &os) const { EcmaString *str = EcmaString::Cast(GetIteratedString().GetTaggedObject()); @@ -2583,6 +2611,12 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, case JSType::JS_API_TREESET_ITERATOR: JSAPITreeSetIterator::Cast(obj)->DumpForSnapshot(thread, vec); return; + case JSType::JS_API_QUEUE: + JSAPIQueue::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_API_QUEUE_ITERATOR: + JSAPIQueueIterator::Cast(obj)->DumpForSnapshot(thread, vec); + return; default: break; } @@ -2990,6 +3024,21 @@ void JSAPIArrayListIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, JSObject::DumpForSnapshot(thread, vec); } +void JSAPIQueue::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); +} + +void JSAPIQueueIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSAPIQueue *queue = JSAPIQueue::Cast(GetIteratedQueue().GetTaggedObject()); + queue->DumpForSnapshot(thread, vec); + vec.push_back(std::make_pair(CString("NextIndex"), JSTaggedValue(GetNextIndex()))); + JSObject::DumpForSnapshot(thread, vec); +} + void JSArrayIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, std::vector> &vec) const { @@ -3173,6 +3222,7 @@ void GlobalEnv::DumpForSnapshot([[maybe_unused]] JSThread *thread, vec.push_back(std::make_pair(CString("ArrayListIteratorPrototype"), globalConst->GetArrayListIteratorPrototype())); vec.push_back(std::make_pair(CString("TreeMapIteratorPrototype"), globalConst->GetTreeMapIteratorPrototype())); vec.push_back(std::make_pair(CString("TreeSetIteratorPrototype"), globalConst->GetTreeSetIteratorPrototype())); + vec.push_back(std::make_pair(CString("QueueIteratorPrototype"), globalConst->GetQueueIteratorPrototype())); } void JSDataView::DumpForSnapshot([[maybe_unused]] JSThread *thread, diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index dfd66def..4a7cb327 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -95,6 +95,7 @@ class JSThread; V(JSTaggedValue, ArrayListIteratorPrototype, ARRAYLIST_ITERATOR_PROTOTYPE_INDEX, ArrayListIterator) \ V(JSTaggedValue, TreeMapIteratorPrototype, TREEMAP_ITERATOR_PROTOTYPE_INDEX, TreeMapIterator) \ V(JSTaggedValue, TreeSetIteratorPrototype, TREESET_ITERATOR_PROTOTYPE_INDEX, TreeSetIterator) \ + V(JSTaggedValue, QueueIteratorPrototype, QUEUE_ITERATOR_PROTOTYPE_INDEX, QueueIterator) \ /* SymbolTable*RegisterSymbols */ \ V(JSTaggedValue, NameString, NAME_STRING_INDEX, name) \ V(JSTaggedValue, GetPrototypeOfString, GETPROTOTYPEOF_STRING_INDEX, getPrototypeOf) \ diff --git a/ecmascript/interpreter/fast_runtime_stub-inl.h b/ecmascript/interpreter/fast_runtime_stub-inl.h index 18f70d40..29e3343b 100644 --- a/ecmascript/interpreter/fast_runtime_stub-inl.h +++ b/ecmascript/interpreter/fast_runtime_stub-inl.h @@ -22,6 +22,7 @@ #include "ecmascript/global_env.h" #include "ecmascript/internal_call_params.h" #include "ecmascript/js_api_arraylist.h" +#include "ecmascript/js_api_queue.h" #include "ecmascript/js_function.h" #include "ecmascript/js_hclass-inl.h" #include "ecmascript/js_proxy.h" @@ -163,7 +164,7 @@ bool FastRuntimeStub::IsSpecialReceiverObj(JSType jsType) bool FastRuntimeStub::IsSpecialContainer(JSType jsType) { - return jsType >= JSType::JS_API_ARRAY_LIST && jsType <= JSType::JS_QUEUE; + return jsType >= JSType::JS_API_ARRAY_LIST && jsType <= JSType::JS_API_QUEUE; } int32_t FastRuntimeStub::TryToElementsIndex(JSTaggedValue key) @@ -1344,6 +1345,9 @@ JSTaggedValue FastRuntimeStub::GetContainerProperty(JSThread *thread, JSTaggedVa case JSType::JS_API_ARRAY_LIST: res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index); break; + case JSType::JS_API_QUEUE: + res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Get(thread, index); + break; default: break; } @@ -1358,6 +1362,9 @@ JSTaggedValue FastRuntimeStub::SetContainerProperty(JSThread *thread, JSTaggedVa case JSType::JS_API_ARRAY_LIST: res = JSAPIArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value); break; + case JSType::JS_API_QUEUE: + res = JSAPIQueue::Cast(receiver.GetTaggedObject())->Set(thread, index, value); + break; default: break; } diff --git a/ecmascript/js_api_queue.cpp b/ecmascript/js_api_queue.cpp new file mode 100644 index 00000000..6b6cd938 --- /dev/null +++ b/ecmascript/js_api_queue.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_api_queue.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "interpreter/fast_runtime_stub-inl.h" + +namespace panda::ecmascript { +void JSAPIQueue::Add(JSThread *thread, const JSHandle &queue, const JSHandle &value) +{ + uint32_t length = queue->GetLength().GetArrayLength(); + JSHandle elements = GrowCapacity(thread, queue, length + 1); + + ASSERT(!elements->IsDictionaryMode()); + uint32_t tail = queue->GetTail(); + + elements->Set(thread, tail, value); + queue->SetLength(thread, JSTaggedValue(++length)); + + uint32_t elementsSize = elements->GetLength(); + ASSERT(elementsSize != 0); + queue->SetTail((tail + 1) % elementsSize); +} + +JSHandle JSAPIQueue::GrowCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity) +{ + JSHandle newElements; + uint32_t front = obj->GetFront(); + uint32_t tail = obj->GetTail(); + JSHandle oldElements(thread, obj->GetElements()); + 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); + front = 0; + tail = oldLength - 1; + } else { + return oldElements; + } + + obj->SetElements(thread, newElements); + obj->SetFront(front); + obj->SetTail(tail); + return newElements; +} + +JSTaggedValue JSAPIQueue::GetFirst(JSThread *thread, const JSHandle &queue) +{ + if (queue->GetLength().GetArrayLength() < 1) { + return JSTaggedValue::Undefined(); + } + + uint32_t index = queue->GetFront(); + + JSHandle elements(thread, queue->GetElements()); + ASSERT(!elements->IsDictionaryMode()); + return elements->Get(index); +} + +JSTaggedValue JSAPIQueue::Pop(JSThread *thread, const JSHandle &queue) +{ + uint32_t length = queue->GetLength().GetArrayLength(); + if (length < 1) { + return JSTaggedValue::Undefined(); + } + + JSHandle elements(thread, queue->GetElements()); + ASSERT(!elements->IsDictionaryMode()); + uint32_t front = queue->GetFront(); + + JSTaggedValue value = elements->Get(front); + queue->SetLength(thread, JSTaggedValue(length-1)); + + uint32_t elementsSize = elements->GetLength(); + ASSERT(elementsSize != 0); + queue->SetFront((front + 1) % elementsSize); + + return value; +} + +JSTaggedValue JSAPIQueue::Get(JSThread *thread, const uint32_t index) +{ + if (index < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Get property index out-of-bounds", JSTaggedValue::Exception()); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + return elements->Get(index); +} + +JSTaggedValue JSAPIQueue::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) +{ + if (index < 0 || index >= GetLength().GetArrayLength()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Set property index out-of-bounds", JSTaggedValue::Exception()); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + elements->Set(thread, index, value); + return JSTaggedValue::Undefined(); +} + +bool JSAPIQueue::Has(JSTaggedValue value) const +{ + uint32_t begin = GetCurrentFront(); + uint32_t end = GetCurrentTail(); + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + uint32_t capacity = elements->GetLength(); + + uint32_t index = begin; + while (index == end) { + 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; + } + return false; +} + +JSHandle JSAPIQueue::OwnKeys(JSThread *thread, const JSHandle &obj) +{ + uint32_t length = obj->GetLength().GetArrayLength(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys = factory->NewTaggedArray(length); + + for (uint32_t i = 0; i < length; i++) { + keys->Set(thread, i, JSTaggedValue(i)); + } + + return keys; +} + +bool JSAPIQueue::GetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); + } + + uint32_t length = obj->GetLength().GetArrayLength(); + 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); +} + +uint32_t JSAPIQueue::GetArrayLength(JSThread *thread, const JSHandle &queue) +{ + uint32_t begin = queue->GetCurrentFront(); + uint32_t end = queue->GetCurrentTail(); + JSHandle elements(thread, queue->GetElements()); + ASSERT(!elements->IsDictionaryMode()); + uint32_t elementsSize = elements->GetLength(); + ASSERT(elementsSize != 0); + uint32_t length = (end - begin + elementsSize) % elementsSize; + return length; +} + +uint32_t JSAPIQueue::GetNextPosition(uint32_t current) +{ + uint32_t next = 0; + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + uint32_t elementsSize = elements->GetLength(); + ASSERT(elementsSize != 0); + next = (current + 1) % elementsSize; + return next; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_queue.h b/ecmascript/js_api_queue.h new file mode 100644 index 00000000..630fb5b0 --- /dev/null +++ b/ecmascript/js_api_queue.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_JSAPIQUEUE_H +#define ECMASCRIPT_JSAPIQUEUE_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSAPIQueue : public JSObject { +public: + static constexpr int DEFAULT_CAPACITY_LENGTH = 8; + static JSAPIQueue *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSAPIQueue()); + return static_cast(object); + } + + static void Add(JSThread *thread, const JSHandle &queue, const JSHandle &value); + static JSTaggedValue GetFirst(JSThread *thread, const JSHandle &queue); + static JSTaggedValue Pop(JSThread *thread, const JSHandle &queue); + static void ForEach(JSThread *thread, const JSHandle &queue, const JSHandle &value); + JSTaggedValue Get(JSThread *thread, const uint32_t index); + + JSTaggedValue Set(JSThread *thread, const uint32_t index, JSTaggedValue value); + 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); + + inline uint32_t GetSize() const + { + return GetLength().GetArrayLength(); + } + + inline uint32_t GetCurrentFront() const + { + return GetFront(); + } + + inline uint32_t GetCurrentTail() const + { + return GetTail(); + } + + static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; + ACCESSORS(Length, LENGTH_OFFSET, FRONT_OFFSET); + ACCESSORS_PRIMITIVE_FIELD(Front, uint32_t, FRONT_OFFSET, TAIL_OFFSET) + ACCESSORS_PRIMITIVE_FIELD(Tail, uint32_t, TAIL_OFFSET, LAST_OFFSET) + DEFINE_ALIGN_SIZE(LAST_OFFSET); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LENGTH_OFFSET, FRONT_OFFSET) + DECL_DUMP() + + static uint32_t GetArrayLength(JSThread *thread, const JSHandle &queue); + + uint32_t GetNextPosition(uint32_t currentPosition); + +private: + inline static uint32_t ComputeCapacity(uint32_t oldCapacity) + { + uint32_t newCapacity = oldCapacity + (oldCapacity >> 1U); + return newCapacity > DEFAULT_CAPACITY_LENGTH ? newCapacity : DEFAULT_CAPACITY_LENGTH; + } + static JSHandle GrowCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSAPIQUEUE_H diff --git a/ecmascript/js_api_queue_iterator.cpp b/ecmascript/js_api_queue_iterator.cpp new file mode 100644 index 00000000..184225fc --- /dev/null +++ b/ecmascript/js_api_queue_iterator.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "js_api_queue_iterator.h" +#include "builtins/builtins_errors.h" +#include "global_env.h" +#include "js_api_queue.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +// QueueIteratorPrototype%.next() +JSTaggedValue JSAPIQueueIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle input(BuiltinsBase::GetThis(argv)); + + if (!input->IsJSAPIQueueIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an queue iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + JSHandle queue(thread, iter->GetIteratedQueue()); + JSHandle undefinedHandle = thread->GlobalConstants()->GetHandledUndefined(); + if (queue->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + + uint32_t index = iter->GetNextIndex(); + uint32_t length = JSAPIQueue::GetArrayLength(thread, JSHandle(queue)); + if (index >= length) { + iter->SetIteratedQueue(thread, undefinedHandle); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + iter->SetNextIndex(index + 1); + + JSHandle key(thread, JSTaggedValue(index)); + JSHandle value = JSTaggedValue::GetProperty(thread, queue, key).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_api_queue_iterator.h b/ecmascript/js_api_queue_iterator.h new file mode 100644 index 00000000..26832e2a --- /dev/null +++ b/ecmascript/js_api_queue_iterator.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_JS_API_QUEUE_ITERATOR_H +#define ECMASCRIPT_JS_API_QUEUE_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSAPIQueueIterator : public JSObject { +public: + static JSAPIQueueIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSAPIQueueIterator()); + return static_cast(obj); + } + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ITERATED_QUEUE_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedQueue, ITERATED_QUEUE_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_QUEUE_OFFSET, NEXT_INDEX_OFFSET) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_API_QUEUE_ITERATOR_H diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index f3fe5dc0..7a06ba80 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -94,6 +94,7 @@ class ProtoChangeDetails; JS_MAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_SET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_ARRAYLIST_ITERATOR, /* /////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_QUEUE_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREEMAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREESET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ JS_ARRAY_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -119,7 +120,7 @@ class ProtoChangeDetails; JS_API_VECTOR, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREE_MAP, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_API_TREE_SET, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_QUEUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_API_QUEUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_TYPED_ARRAY, /* JS_TYPED_ARRAY_BEGIN /////////////////////////////////////////////////////////////////// */ \ JS_INT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_UINT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -613,7 +614,7 @@ public: // non ECMA standard jsapi containers. inline bool IsSpecialContainer() const { - return GetObjectType() >= JSType::JS_API_ARRAY_LIST && GetObjectType() <= JSType::JS_QUEUE; + return GetObjectType() >= JSType::JS_API_ARRAY_LIST && GetObjectType() <= JSType::JS_API_QUEUE; } inline bool IsJSAPIArrayList() const { @@ -623,10 +624,15 @@ public: { return GetObjectType() == JSType::JS_API_ARRAYLIST_ITERATOR; } - inline bool IsJSQueue() const + inline bool IsJSAPIQueue() const { - return GetObjectType() == JSType::JS_QUEUE; + return GetObjectType() == JSType::JS_API_QUEUE; } + inline bool IsJSAPIQueueIterator() const + { + return GetObjectType() == JSType::JS_API_QUEUE_ITERATOR; + } + inline bool IsJSAPITreeMap() const { return GetObjectType() == JSType::JS_API_TREE_MAP; diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index f70001c7..5ce447e0 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -512,6 +512,11 @@ inline bool JSTaggedValue::IsJSAPITreeSet() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPITreeSet(); } +inline bool JSTaggedValue::IsJSAPIQueue() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIQueue(); +} + inline bool JSTaggedValue::IsSpecialContainer() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsSpecialContainer(); @@ -800,6 +805,11 @@ inline bool JSTaggedValue::IsJSAPIArrayListIterator() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIArrayListIterator(); } +inline bool JSTaggedValue::IsJSAPIQueueIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIQueueIterator(); +} + inline bool JSTaggedValue::IsIterator() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsIterator(); diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp index 322b07c4..3520e6b1 100644 --- a/ecmascript/js_tagged_value.cpp +++ b/ecmascript/js_tagged_value.cpp @@ -18,6 +18,7 @@ #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/internal_call_params.h" +#include "ecmascript/js_api_queue.h" #include "ecmascript/js_array.h" #include "ecmascript/js_api_arraylist.h" #include "ecmascript/js_handle.h" @@ -782,8 +783,9 @@ bool JSTaggedValue::HasContainerProperty(JSThread *thread, const JSHandle::Cast(obj)->Has(key.GetTaggedValue()); } - case JSType::JS_QUEUE: - break; + case JSType::JS_API_QUEUE: { + return JSHandle::Cast(obj)->Has(key.GetTaggedValue()); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::HasProperty(thread, JSHandle(obj), key); @@ -803,8 +805,9 @@ JSHandle JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *threa case JSType::JS_API_ARRAY_LIST: { return JSAPIArrayList::OwnKeys(thread, JSHandle::Cast(obj)); } - case JSType::JS_QUEUE: - break; + case JSType::JS_API_QUEUE: { + return JSAPIQueue::OwnKeys(thread, JSHandle::Cast(obj)); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); @@ -825,8 +828,9 @@ bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle::Cast(obj), key, desc); } - case JSType::JS_QUEUE: - break; + case JSType::JS_API_QUEUE: { + return JSAPIQueue::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + } case JSType::JS_API_TREE_MAP: case JSType::JS_API_TREE_SET: { return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index 2cb9148a..be26610d 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -323,6 +323,8 @@ public: bool IsJSAPITreeSet() const; bool IsJSAPITreeMapIterator() const; bool IsJSAPITreeSetIterator() const; + bool IsJSAPIQueue() const; + bool IsJSAPIQueueIterator() const; bool IsSpecialContainer() const; bool IsPrototypeHandler() const; diff --git a/ecmascript/mem/object_xray-inl.h b/ecmascript/mem/object_xray-inl.h index 7357f1be..07220fa4 100644 --- a/ecmascript/mem/object_xray-inl.h +++ b/ecmascript/mem/object_xray-inl.h @@ -26,6 +26,8 @@ #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_queue.h" +#include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_tree_map.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set.h" @@ -302,7 +304,12 @@ void ObjectXRay::VisitObjectBody(TaggedObject *object, JSHClass *klass, const Ec case JSType::CLASS_INFO_EXTRACTOR: ClassInfoExtractor::Cast(object)->VisitRangeSlot(visitor); break; - case JSType::JS_QUEUE: + case JSType::JS_API_QUEUE: + JSAPIQueue::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_API_QUEUE_ITERATOR: + JSAPIQueueIterator::Cast(object)->VisitRangeSlot(visitor); + break; case JSType::JS_API_ARRAY_LIST: JSAPIArrayList::Cast(object)->VisitRangeSlot(visitor); break; diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index 6f446c21..020cfd63 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -36,6 +36,8 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_queue.h" +#include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_tree_map.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set.h" @@ -788,6 +790,11 @@ JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandleSetTreeSet(thread_, JSTaggedValue::Undefined()); break; + case JSType::JS_API_QUEUE: + JSAPIQueue::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + JSAPIQueue::Cast(*obj)->SetFront(0); + JSAPIQueue::Cast(*obj)->SetTail(0); + break; case JSType::JS_FUNCTION: case JSType::JS_GENERATOR_FUNCTION: case JSType::JS_FORIN_ITERATOR: @@ -796,6 +803,7 @@ JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandle ObjectFactory::NewJSAPIArrayListIterator(const return iter; } +JSHandle ObjectFactory::CopyQueue(const JSHandle &old, [[maybe_unused]] uint32_t oldLength, + uint32_t newLength, uint32_t front, uint32_t tail) +{ + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heap_->AllocateYoungOrHugeObject( + JSHClass::Cast(thread_->GlobalConstants()->GetArrayClass().GetTaggedObject()), size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (uint32_t i = 0; i < oldLength; i++) { + JSTaggedValue value = old->Get(i); + newArray->Set(thread_, i, value); + } + + return newArray; +} + +JSHandle ObjectFactory::NewJSAPIQueueIterator(const JSHandle &queue) +{ + NewObjectHook(); + JSHandle protoValue(thread_, thread_->GlobalConstants()->GetQueueIteratorPrototype()); + JSHandle dynHandle = NewEcmaDynClass(JSAPIQueueIterator::SIZE, JSType::JS_API_QUEUE_ITERATOR, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedQueue(thread_, queue); // IteratedQueue + iter->SetNextIndex(0); + return iter; +} + JSHandle ObjectFactory::NewJSAPITreeMapIterator(const JSHandle &map, IterationKind kind) { diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index efddcdbd..83fed8a0 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -93,6 +93,8 @@ class TSObjLayoutInfo; class TSModuleTable; class JSAPIArrayList; class JSAPIArrayListIterator; +class JSAPIQueue; +class JSAPIQueueIterator; class JSAPITreeSet; class JSAPITreeMap; class JSAPITreeSetIterator; @@ -381,6 +383,9 @@ public: // It is used to provide iterators for non ECMA standard jsapi containers. JSHandle NewJSAPIArrayList(uint32_t capacity); + JSHandle CopyQueue(const JSHandle &old, uint32_t oldLength, + uint32_t newLength, uint32_t front, uint32_t tail); + JSHandle NewJSAPIQueueIterator(const JSHandle &queue); JSHandle NewJSAPIArrayListIterator(const JSHandle &arrayList); JSHandle NewJSAPITreeMapIterator(const JSHandle &map, IterationKind kind); JSHandle NewJSAPITreeSetIterator(const JSHandle &set, IterationKind kind); diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h index 561e1596..80bf807c 100644 --- a/ecmascript/runtime_call_id.h +++ b/ecmascript/runtime_call_id.h @@ -574,7 +574,14 @@ namespace panda::ecmascript { V(TreeSet, GetLength) \ V(TreeSet, Values) \ V(TreeSet, ForEach) \ - V(TreeSet, Entries) + V(TreeSet, Entries) \ + V(Queue, Constructor) \ + V(Queue, Add) \ + V(Queue, GetFirst) \ + V(Queue, Pop) \ + V(Queue, ForEach) \ + V(Queue, GetIteratorObj) \ + V(Queue, GetSize) #define ABSTRACT_OPERATION_LIST(V) \ V(JSTaggedValue, ToString) \ diff --git a/ecmascript/snapshot/mem/snapshot_serialize.cpp b/ecmascript/snapshot/mem/snapshot_serialize.cpp index 4ce6bc79..962a9feb 100644 --- a/ecmascript/snapshot/mem/snapshot_serialize.cpp +++ b/ecmascript/snapshot/mem/snapshot_serialize.cpp @@ -54,9 +54,11 @@ #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/class_linker/program_object.h" #include "ecmascript/containers/containers_arraylist.h" +#include "ecmascript/containers/containers_queue.h" #include "ecmascript/containers/containers_treemap.h" #include "ecmascript/containers/containers_treeset.h" #include "ecmascript/global_env.h" +#include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set_iterator.h" #include "ecmascript/js_array_iterator.h" @@ -119,6 +121,7 @@ using PluralRules = builtins::BuiltinsPluralRules; using ArrayList = containers::ContainersArrayList; using TreeMap = containers::ContainersTreeMap; using TreeSet = containers::ContainersTreeSet; +using Queue = containers::ContainersQueue; constexpr int TAGGED_SIZE = JSTaggedValue::TaggedTypeSize(); constexpr int OBJECT_HEADER_SIZE = TaggedObject::TaggedObjectSize(); @@ -616,6 +619,14 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(TreeSet::GetLength), reinterpret_cast(JSAPITreeMapIterator::Next), reinterpret_cast(JSAPITreeSetIterator::Next), + reinterpret_cast(Queue::QueueConstructor), + reinterpret_cast(Queue::Add), + reinterpret_cast(Queue::GetFirst), + reinterpret_cast(Queue::Pop), + reinterpret_cast(Queue::ForEach), + reinterpret_cast(Queue::GetIteratorObj), + reinterpret_cast(Queue::GetSize), + reinterpret_cast(JSAPIQueueIterator::Next), // not builtins method reinterpret_cast(JSFunction::PrototypeSetter), diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index 491e5d68..d8cdcb33 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -30,6 +30,8 @@ #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_api_queue.h" +#include "ecmascript/js_api_queue_iterator.h" #include "ecmascript/js_api_tree_map.h" #include "ecmascript/js_api_tree_map_iterator.h" #include "ecmascript/js_api_tree_set.h" @@ -192,6 +194,18 @@ static JSHandle NewJSAPIArrayList(JSThread *thread, ObjectFactor return jsArrayList; } +static JSHandle NewJSAPIQueue(JSThread *thread, ObjectFactory *factory, JSHandle proto) +{ + JSHandle queueClass = factory->NewEcmaDynClass(JSAPIQueue::SIZE, JSType::JS_API_QUEUE, proto); + JSHandle jsQueue = JSHandle::Cast(factory->NewJSObject(queueClass)); + JSHandle newElements = factory->NewTaggedArray(JSAPIQueue::DEFAULT_CAPACITY_LENGTH); + jsQueue->SetLength(thread, JSTaggedValue(0)); + jsQueue->SetFront(0); + jsQueue->SetTail(0); + jsQueue->SetElements(thread, newElements); + return jsQueue; +} + HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) { [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); @@ -688,7 +702,6 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) DUMP_FOR_HANDLE(unionType) break; } - case JSType::JS_QUEUE: case JSType::JS_API_VECTOR: case JSType::JS_API_ARRAY_LIST: { // 1 : 1 dump fileds number @@ -705,6 +718,22 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) DUMP_FOR_HANDLE(jsArrayListIter) break; } + case JSType::JS_API_QUEUE: { + // 2 : 2 dump fileds number + CHECK_DUMP_FILEDS(JSObject::SIZE, JSAPIQueue::SIZE, 2) + JSHandle jsQueue = NewJSAPIQueue(thread, factory, proto); + DUMP_FOR_HANDLE(jsQueue) + break; + } + case JSType::JS_API_QUEUE_ITERATOR: { + // 2 : 2 dump fileds number + CHECK_DUMP_FILEDS(JSObject::SIZE, JSAPIQueueIterator::SIZE, 2) + JSHandle jsQueue = NewJSAPIQueue(thread, factory, proto); + JSHandle jsQueueIter = + factory->NewJSAPIQueueIterator(jsQueue); + DUMP_FOR_HANDLE(jsQueueIter) + break; + } case JSType::JS_API_TREE_MAP: { // 1 : 1 dump fileds number CHECK_DUMP_FILEDS(JSObject::SIZE, JSAPITreeMap::SIZE, 1) diff --git a/ecmascript/trampoline/ecma_asm_defines.h b/ecmascript/trampoline/ecma_asm_defines.h index 4f501b08..a7b0eef1 100644 --- a/ecmascript/trampoline/ecma_asm_defines.h +++ b/ecmascript/trampoline/ecma_asm_defines.h @@ -17,13 +17,13 @@ #define ECMASCRIPT_ASM_DEFINES_H #ifdef PANDA_TARGET_64 -#define ASM_GLUE_CURRENT_FRAME_OFFSET 2128 -#define ASM_GLUE_RUNTIME_FUNCTIONS_OFFSET 4192 +#define ASM_GLUE_CURRENT_FRAME_OFFSET 2136 +#define ASM_GLUE_RUNTIME_FUNCTIONS_OFFSET 4200 #endif #ifdef PANDA_TARGET_32 -#define ASM_GLUE_CURRENT_FRAME_OFFSET 2120 -#define ASM_GLUE_RUNTIME_FUNCTIONS_OFFSET 3152 +#define ASM_GLUE_CURRENT_FRAME_OFFSET 2128 +#define ASM_GLUE_RUNTIME_FUNCTIONS_OFFSET 3160 #endif #define OPTIMIZE_FRAME_TYPE 0