diff --git a/ecmascript/compiler/builtins/builtins_call_signature.h b/ecmascript/compiler/builtins/builtins_call_signature.h index afa69b8ebd..668c1d14a0 100644 --- a/ecmascript/compiler/builtins/builtins_call_signature.h +++ b/ecmascript/compiler/builtins/builtins_call_signature.h @@ -33,6 +33,10 @@ namespace panda::ecmascript::kungfu { V(DequeForEach) \ V(LightWeightMapForEach) \ V(LightWeightSetForEach) \ + V(HashMapForEach) \ + V(HashSetForEach) \ + V(LinkedListForEach) \ + V(ListForEach) \ V(ArrayListForEach) \ V(ArrayListReplaceAllElements) \ diff --git a/ecmascript/compiler/builtins/builtins_stubs.cpp b/ecmascript/compiler/builtins/builtins_stubs.cpp index 407494face..c5a99ec069 100644 --- a/ecmascript/compiler/builtins/builtins_stubs.cpp +++ b/ecmascript/compiler/builtins/builtins_stubs.cpp @@ -643,6 +643,90 @@ DECLARE_BUILTINS(LightWeightSetForEach) Return(*res); } +DECLARE_BUILTINS(HashMapForEach) +{ + auto env = GetEnvironment(); + DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); + + Label exit(env); + Label slowPath(env); + + ContainersStubBuilder containersBuilder(this); + containersBuilder.ContainersHashCall(glue, thisValue, numArgs, &res, &exit, + &slowPath, ContainersType::HASHMAP_FOREACH); + Bind(&slowPath); + { + BUILDARG(); + res = CALLSLOWPATH(); + Jump(&exit); + } + Bind(&exit); + Return(*res); +} + +DECLARE_BUILTINS(HashSetForEach) +{ + auto env = GetEnvironment(); + DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); + + Label exit(env); + Label slowPath(env); + + ContainersStubBuilder containersBuilder(this); + containersBuilder.ContainersHashCall(glue, thisValue, numArgs, &res, &exit, + &slowPath, ContainersType::HASHSET_FOREACH); + Bind(&slowPath); + { + BUILDARG(); + res = CALLSLOWPATH(); + Jump(&exit); + } + Bind(&exit); + Return(*res); +} + +DECLARE_BUILTINS(LinkedListForEach) +{ + auto env = GetEnvironment(); + DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); + + Label exit(env); + Label slowPath(env); + + ContainersStubBuilder containersBuilder(this); + containersBuilder.ContainersLinkedListCall(glue, thisValue, numArgs, &res, &exit, + &slowPath, ContainersType::LINKEDLIST_FOREACH); + Bind(&slowPath); + { + BUILDARG(); + res = CALLSLOWPATH(); + Jump(&exit); + } + Bind(&exit); + Return(*res); +} + +DECLARE_BUILTINS(ListForEach) +{ + auto env = GetEnvironment(); + DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); + + Label exit(env); + Label slowPath(env); + + ContainersStubBuilder containersBuilder(this); + containersBuilder.ContainersLinkedListCall(glue, thisValue, numArgs, &res, &exit, + &slowPath, ContainersType::LIST_FOREACH); + Bind(&slowPath); + { + BUILDARG(); + res = CALLSLOWPATH(); + Jump(&exit); + } + Bind(&exit); + Return(*res); +} + DECLARE_BUILTINS(ArrayListForEach) { auto env = GetEnvironment(); diff --git a/ecmascript/compiler/builtins/containers_hashmap_stub_builder.h b/ecmascript/compiler/builtins/containers_hashmap_stub_builder.h new file mode 100644 index 0000000000..4d7608868e --- /dev/null +++ b/ecmascript/compiler/builtins/containers_hashmap_stub_builder.h @@ -0,0 +1,46 @@ +/* + * 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_COMPILER_BUILTINS_CONTAINERS_HASHMAP_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_HASHMAP_STUB_BUILDER_H +#include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/js_api/js_api_hashmap.h" + +namespace panda::ecmascript::kungfu { +class ContainersHashMapStubBuilder : public StubBuilder { +public: + explicit ContainersHashMapStubBuilder(StubBuilder *parent) + : StubBuilder(parent) {} + ~ContainersHashMapStubBuilder() = default; + NO_MOVE_SEMANTIC(ContainersHashMapStubBuilder); + NO_COPY_SEMANTIC(ContainersHashMapStubBuilder); + void GenerateCircuit() override {} + + GateRef GetTableLength(GateRef obj) + { + GateRef tableOffset = IntPtr(JSAPIHashMap::HASHMAP_TABLE_INDEX); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + return GetLengthOfTaggedArray(table); + } + + GateRef GetNode(GateRef obj, GateRef index) + { + GateRef tableOffset = IntPtr(JSAPIHashMap::HASHMAP_TABLE_INDEX); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + return GetValueFromTaggedArray(table, index); + } +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_HASHMAP_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/containers_hashset_stub_builder.h b/ecmascript/compiler/builtins/containers_hashset_stub_builder.h new file mode 100644 index 0000000000..701388fc13 --- /dev/null +++ b/ecmascript/compiler/builtins/containers_hashset_stub_builder.h @@ -0,0 +1,46 @@ +/* + * 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_COMPILER_BUILTINS_CONTAINERS_HASHSET_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_HASHSET_STUB_BUILDER_H +#include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/js_api/js_api_hashset.h" + +namespace panda::ecmascript::kungfu { +class ContainersHashSetStubBuilder : public StubBuilder { +public: + explicit ContainersHashSetStubBuilder(StubBuilder *parent) + : StubBuilder(parent) {} + ~ContainersHashSetStubBuilder() = default; + NO_MOVE_SEMANTIC(ContainersHashSetStubBuilder); + NO_COPY_SEMANTIC(ContainersHashSetStubBuilder); + void GenerateCircuit() override {} + + GateRef GetTableLength(GateRef obj) + { + GateRef tableOffset = IntPtr(JSAPIHashSet::HASHSET_TABLE_INDEX); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + return GetLengthOfTaggedArray(table); + } + + GateRef GetNode(GateRef obj, GateRef index) + { + GateRef tableOffset = IntPtr(JSAPIHashSet::HASHSET_TABLE_INDEX); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + return GetValueFromTaggedArray(table, index); + } +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_HASHSET_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/containers_lightweightmap_stub_builder.h b/ecmascript/compiler/builtins/containers_lightweightmap_stub_builder.h index 16328c3164..5dd971ee62 100644 --- a/ecmascript/compiler/builtins/containers_lightweightmap_stub_builder.h +++ b/ecmascript/compiler/builtins/containers_lightweightmap_stub_builder.h @@ -48,4 +48,4 @@ public: } }; } // namespace panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_VECTOR_STUB_BUILDER_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_LIGHTWEIGHTMAP_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/containers_lightweightset_stub_builder.h b/ecmascript/compiler/builtins/containers_lightweightset_stub_builder.h index eb6f6dcc4b..40c0c906be 100644 --- a/ecmascript/compiler/builtins/containers_lightweightset_stub_builder.h +++ b/ecmascript/compiler/builtins/containers_lightweightset_stub_builder.h @@ -46,4 +46,4 @@ public: } }; } // namespace panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_VECTOR_STUB_BUILDER_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_LIGHTWEIGHTSET_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/containers_linkedlist_stub_builder.h b/ecmascript/compiler/builtins/containers_linkedlist_stub_builder.h new file mode 100644 index 0000000000..97016b06c0 --- /dev/null +++ b/ecmascript/compiler/builtins/containers_linkedlist_stub_builder.h @@ -0,0 +1,47 @@ +/* + * 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_COMPILER_BUILTINS_CONTAINERS_LINKEDLIST_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_LINKEDLIST_STUB_BUILDER_H +#include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/js_api/js_api_linked_list.h" + +namespace panda::ecmascript::kungfu { +class ContainersLinkedListStubBuilder : public StubBuilder { +public: + explicit ContainersLinkedListStubBuilder(StubBuilder *parent) + : StubBuilder(parent) {} + ~ContainersLinkedListStubBuilder() = default; + NO_MOVE_SEMANTIC(ContainersLinkedListStubBuilder); + NO_COPY_SEMANTIC(ContainersLinkedListStubBuilder); + void GenerateCircuit() override {} + + GateRef GetTableLength(GateRef obj) + { + GateRef tableOffset = IntPtr(JSAPILinkedList::DOUBLE_LIST_OFFSET); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + GateRef value = GetValueFromTaggedArray(table, Int32(TaggedDoubleList::NUMBER_OF_NODE_INDEX)); + return TaggedGetInt(value); + } + + GateRef GetNode(GateRef obj, GateRef index) + { + GateRef tableOffset = IntPtr(JSAPILinkedList::DOUBLE_LIST_OFFSET); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + return GetValueFromTaggedArray(table, index); + } +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_LINKEDLIST_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/containers_list_stub_builder.h b/ecmascript/compiler/builtins/containers_list_stub_builder.h new file mode 100644 index 0000000000..898d9b1f67 --- /dev/null +++ b/ecmascript/compiler/builtins/containers_list_stub_builder.h @@ -0,0 +1,47 @@ +/* + * 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_COMPILER_BUILTINS_CONTAINERS_LIST_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_LIST_STUB_BUILDER_H +#include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/js_api/js_api_list.h" + +namespace panda::ecmascript::kungfu { +class ContainersListStubBuilder : public StubBuilder { +public: + explicit ContainersListStubBuilder(StubBuilder *parent) + : StubBuilder(parent) {} + ~ContainersListStubBuilder() = default; + NO_MOVE_SEMANTIC(ContainersListStubBuilder); + NO_COPY_SEMANTIC(ContainersListStubBuilder); + void GenerateCircuit() override {} + + GateRef GetTableLength(GateRef obj) + { + GateRef tableOffset = IntPtr(JSAPIList::SINGLY_LIST_OFFSET); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + GateRef value = GetValueFromTaggedArray(table, Int32(TaggedSingleList::NUMBER_OF_NODE_INDEX)); + return TaggedGetInt(value); + } + + GateRef GetNode(GateRef obj, GateRef index) + { + GateRef tableOffset = IntPtr(JSAPIList::SINGLY_LIST_OFFSET); + GateRef table = Load(VariableType::JS_POINTER(), obj, tableOffset); + return GetValueFromTaggedArray(table, index); + } +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_LIST_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/containers_stub_builder.cpp b/ecmascript/compiler/builtins/containers_stub_builder.cpp index 80275c1088..1c23188576 100644 --- a/ecmascript/compiler/builtins/containers_stub_builder.cpp +++ b/ecmascript/compiler/builtins/containers_stub_builder.cpp @@ -456,4 +456,247 @@ void ContainersStubBuilder::ContainersLightWeightCall(GateRef glue, GateRef this Bind(&afterLoop); Jump(exit); } + +void ContainersStubBuilder::ContainersHashCall(GateRef glue, GateRef thisValue, + GateRef numArgs, Variable* result, Label *exit, Label *slowPath, ContainersType type) +{ + auto env = GetEnvironment(); + DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue); + DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(node, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(key, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(value, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(length, VariableType::INT32(), Int32(0)); + DEFVARIABLE(index, VariableType::INT32(), Int32(0)); + Label valueIsJSAPIHash(env); + Label valueNotJSAPIHash(env); + Label objIsJSProxy(env); + Label objNotJSProxy(env); + Label objIsJSAPIHash(env); + Label thisArgUndefined(env); + Label thisArgNotUndefined(env); + Label callbackUndefined(env); + Label callbackNotUndefined(env); + Label nextCount(env); + Label nodeNotHole(env); + Label nodeIsLinked(env); + Label nodeIsRBTree(env); + Label loopLinked(env); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label afterLoop(env); + GateRef callbackFnHandle; + Branch(IsContainer(*thisObj, type), &valueIsJSAPIHash, &valueNotJSAPIHash); + Bind(&valueNotJSAPIHash); + { + Branch(IsJsProxy(*thisObj), &objIsJSProxy, &objNotJSProxy); + Bind(&objIsJSProxy); + { + GateRef tempObj = GetTarget(*thisObj); + Branch(IsContainer(tempObj, type), &objIsJSAPIHash, slowPath); + Bind(&objIsJSAPIHash); + { + thisObj = tempObj; + Jump(&valueIsJSAPIHash); + } + } + Bind(&objNotJSProxy); + Jump(slowPath); + } + Bind(&valueIsJSAPIHash); + { + Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined); + Bind(&callbackUndefined); + Jump(slowPath); + Bind(&callbackNotUndefined); + { + Label isCall(env); + Label notCall(env); + Label isHeapObj(env); + callbackFnHandle = GetCallArg0(); + Branch(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, ¬Call); + Bind(&isHeapObj); + Branch(IsCallable(callbackFnHandle), &isCall, ¬Call); + Bind(¬Call); + Jump(slowPath); + Bind(&isCall); + { + Branch(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined); + Bind(&thisArgUndefined); + Jump(&nextCount); + Bind(&thisArgNotUndefined); + thisArg = GetCallArg1(); + Jump(&nextCount); + } + } + } + Bind(&nextCount); + { + length = ContainerGetSize(*thisObj, type); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label hasExceptionLinked(env); + Label notHasExceptionLinked(env); + Label hasExceptionRBTree(env); + Label notHasExceptionRBTree(env); + Branch(Int32LessThan(*index, *length), &next, &afterLoop); + Bind(&next); + { + node = ContainerGetNode(*thisObj, *index, type); + Branch(TaggedIsHole(*node), &loopEnd, &nodeNotHole); + Bind(&nodeNotHole); + Branch(IsLinkedNode(*node), &nodeIsLinked, &nodeIsRBTree); + LoopBegin(&nodeIsLinked); + { + value = Load(VariableType::JS_POINTER(), *node, IntPtr( + type == ContainersType::HASHSET_FOREACH ? LinkedNode::KEY_OFFSET : LinkedNode::VALUE_OFFSET)); + key = Load(VariableType::JS_POINTER(), *node, IntPtr(LinkedNode::KEY_OFFSET)); + GateRef retValue = JSCallDispatch(glue, callbackFnHandle, Int32(3), 0, // 3: numArgs + JSCallMode::CALL_THIS_ARG3_WITH_RETURN, { *thisArg, *value, *key, *thisObj }); + Branch(HasPendingException(glue), &hasExceptionLinked, ¬HasExceptionLinked); + Bind(&hasExceptionLinked); + { + result->WriteVariable(retValue); + Jump(exit); + } + Bind(¬HasExceptionLinked); + node = Load(VariableType::JS_POINTER(), *node, IntPtr(LinkedNode::NEXT_OFFSET)); + Branch(TaggedIsHole(*node), &loopEnd, &loopLinked); + } + Bind(&loopLinked); + LoopEnd(&nodeIsLinked); + Bind(&nodeIsRBTree); + { + GateRef retValue = CallRuntime(glue, RTSTUB_ID(ContainerRBTreeForEach), + { *node, callbackFnHandle, *thisArg, *thisObj, + IntToTaggedInt(Int32(static_cast(type))) }); + Branch(HasPendingException(glue), &hasExceptionRBTree, ¬HasExceptionRBTree); + Bind(&hasExceptionRBTree); + { + result->WriteVariable(retValue); + Jump(exit); + } + Bind(¬HasExceptionRBTree); + Jump(&loopEnd); + } + } + } + Bind(&loopEnd); + index = Int32Add(*index, Int32(1)); + LoopEnd(&loopHead); + } + Bind(&afterLoop); + Jump(exit); +} + +void ContainersStubBuilder::ContainersLinkedListCall(GateRef glue, GateRef thisValue, + GateRef numArgs, Variable* result, Label *exit, Label *slowPath, ContainersType type) +{ + auto env = GetEnvironment(); + DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue); + DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(valueNode, VariableType::INT32(), Int32(0)); + DEFVARIABLE(key, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(value, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(length, VariableType::INT32(), Int32(0)); + DEFVARIABLE(index, VariableType::INT32(), Int32(0)); + Label valueIsJSAPILinkedList(env); + Label valueNotJSAPILinkedList(env); + Label objIsJSProxy(env); + Label objNotJSProxy(env); + Label objIsJSAPILinkedList(env); + Label thisArgUndefined(env); + Label thisArgNotUndefined(env); + Label callbackUndefined(env); + Label callbackNotUndefined(env); + Label nextCount(env); + Label valueNotHole(env); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label afterLoop(env); + GateRef callbackFnHandle; + Branch(IsContainer(*thisObj, type), &valueIsJSAPILinkedList, &valueNotJSAPILinkedList); + Bind(&valueNotJSAPILinkedList); + { + Branch(IsJsProxy(*thisObj), &objIsJSProxy, &objNotJSProxy); + Bind(&objIsJSProxy); + { + GateRef tempObj = GetTarget(*thisObj); + Branch(IsContainer(tempObj, type), &objIsJSAPILinkedList, slowPath); + Bind(&objIsJSAPILinkedList); + { + thisObj = tempObj; + Jump(&valueIsJSAPILinkedList); + } + } + Bind(&objNotJSProxy); + Jump(slowPath); + } + Bind(&valueIsJSAPILinkedList); + { + Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined); + Bind(&callbackUndefined); + Jump(slowPath); + Bind(&callbackNotUndefined); + { + Label isCall(env); + Label notCall(env); + Label isHeapObj(env); + callbackFnHandle = GetCallArg0(); + Branch(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, ¬Call); + Bind(&isHeapObj); + Branch(IsCallable(callbackFnHandle), &isCall, ¬Call); + Bind(¬Call); + Jump(slowPath); + Bind(&isCall); + { + Branch(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined); + Bind(&thisArgUndefined); + Jump(&nextCount); + Bind(&thisArgNotUndefined); + thisArg = GetCallArg1(); + Jump(&nextCount); + } + } + } + Bind(&nextCount); + { + length = ContainerGetSize(*thisObj, type); + valueNode = Int32(TaggedList::ELEMENTS_START_INDEX); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label hasException(env); + Label notHasException(env); + Branch(Int32LessThan(*index, *length), &next, &afterLoop); + Bind(&next); + { + valueNode = TaggedGetInt(ContainerGetNode(*thisObj, + Int32Add(*valueNode, Int32(TaggedList::NEXT_PTR_OFFSET)), type)); + value = ContainerGetNode(*thisObj, *valueNode, type); + Branch(TaggedIsHole(*value), &loopEnd, &valueNotHole); + Bind(&valueNotHole); + key = IntToTaggedInt(*index); + GateRef retValue = JSCallDispatch(glue, callbackFnHandle, Int32(3), 0, // 3: numArgs + JSCallMode::CALL_THIS_ARG3_WITH_RETURN, { *thisArg, *value, *key, *thisObj }); + Branch(HasPendingException(glue), &hasException, ¬HasException); + Bind(&hasException); + { + result->WriteVariable(retValue); + Jump(exit); + } + Bind(¬HasException); + Jump(&loopEnd); + } + } + Bind(&loopEnd); + index = Int32Add(*index, Int32(1)); + LoopEnd(&loopHead); + } + Bind(&afterLoop); + Jump(exit); +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/containers_stub_builder.h b/ecmascript/compiler/builtins/containers_stub_builder.h index 76227d1d8d..2d0ee92851 100644 --- a/ecmascript/compiler/builtins/containers_stub_builder.h +++ b/ecmascript/compiler/builtins/containers_stub_builder.h @@ -17,8 +17,12 @@ #define ECMASCRIPT_COMPILER_BUILTINS_CONTAINERS_STUB_BUILDER_H #include "ecmascript/compiler/builtins/containers_arraylist_stub_builder.h" #include "ecmascript/compiler/builtins/containers_deque_stub_builder.h" +#include "ecmascript/compiler/builtins/containers_hashmap_stub_builder.h" +#include "ecmascript/compiler/builtins/containers_hashset_stub_builder.h" #include "ecmascript/compiler/builtins/containers_lightweightmap_stub_builder.h" #include "ecmascript/compiler/builtins/containers_lightweightset_stub_builder.h" +#include "ecmascript/compiler/builtins/containers_linkedlist_stub_builder.h" +#include "ecmascript/compiler/builtins/containers_list_stub_builder.h" #include "ecmascript/compiler/builtins/containers_plainarray_stub_builder.h" #include "ecmascript/compiler/builtins/containers_queue_stub_builder.h" #include "ecmascript/compiler/builtins/containers_stack_stub_builder.h" @@ -37,6 +41,10 @@ enum class ContainersType : uint8_t { DEQUE_FOREACH, LIGHTWEIGHTMAP_FOREACH, LIGHTWEIGHTSET_FOREACH, + HASHMAP_FOREACH, + HASHSET_FOREACH, + LINKEDLIST_FOREACH, + LIST_FOREACH, ARRAYLIST_FOREACH, ARRAYLIST_REPLACEALLELEMENTS, }; @@ -62,6 +70,12 @@ public: void ContainersLightWeightCall(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* result, Label *exit, Label *slowPath, ContainersType type); + void ContainersHashCall(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable* result, Label *exit, Label *slowPath, ContainersType type); + + void ContainersLinkedListCall(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable* result, Label *exit, Label *slowPath, ContainersType type); + GateRef IsContainer(GateRef obj, ContainersType type) { switch (type) { @@ -80,6 +94,14 @@ public: return IsJSAPILightWeightMap(obj); case ContainersType::LIGHTWEIGHTSET_FOREACH: return IsJSAPILightWeightSet(obj); + case ContainersType::HASHMAP_FOREACH: + return IsJSAPIHashMap(obj); + case ContainersType::HASHSET_FOREACH: + return IsJSAPIHashSet(obj); + case ContainersType::LINKEDLIST_FOREACH: + return IsJSAPILinkedList(obj); + case ContainersType::LIST_FOREACH: + return IsJSAPIList(obj); case ContainersType::ARRAYLIST_FOREACH: case ContainersType::ARRAYLIST_REPLACEALLELEMENTS: return IsJSAPIArrayList(obj); @@ -177,6 +199,22 @@ public: ContainersLightWeightSetStubBuilder lightWeightSetBuilder(this); return lightWeightSetBuilder.GetSize(obj); } + case ContainersType::HASHMAP_FOREACH: { + ContainersHashMapStubBuilder hashMapBuilder(this); + return hashMapBuilder.GetTableLength(obj); + } + case ContainersType::HASHSET_FOREACH: { + ContainersHashSetStubBuilder hashSetBuilder(this); + return hashSetBuilder.GetTableLength(obj); + } + case ContainersType::LINKEDLIST_FOREACH: { + ContainersLinkedListStubBuilder linkedListBuilder(this); + return linkedListBuilder.GetTableLength(obj); + } + case ContainersType::LIST_FOREACH: { + ContainersListStubBuilder listBuilder(this); + return listBuilder.GetTableLength(obj); + } case ContainersType::ARRAYLIST_REPLACEALLELEMENTS: case ContainersType::ARRAYLIST_FOREACH: { ContainersArrayListStubBuilder arrayListBuilder(this); @@ -248,6 +286,31 @@ public: return False(); } + GateRef ContainerGetNode(GateRef obj, GateRef index, ContainersType type) + { + switch (type) { + case ContainersType::HASHMAP_FOREACH: { + ContainersHashMapStubBuilder hashMapBuilder(this); + return hashMapBuilder.GetNode(obj, index); + } + case ContainersType::HASHSET_FOREACH: { + ContainersHashSetStubBuilder hashSetBuilder(this); + return hashSetBuilder.GetNode(obj, index); + } + case ContainersType::LINKEDLIST_FOREACH: { + ContainersLinkedListStubBuilder linkedListBuilder(this); + return linkedListBuilder.GetNode(obj, index); + } + case ContainersType::LIST_FOREACH: { + ContainersListStubBuilder listBuilder(this); + return listBuilder.GetNode(obj, index); + } + default: + UNREACHABLE(); + } + return False(); + } + GateRef PlainArrayGetKey(GateRef obj, GateRef index) { ContainersPlainArrayStubBuilder plainArrayBuilder(this); diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 0cc1a0f69f..68a49f46db 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -1092,6 +1092,36 @@ inline GateRef StubBuilder::IsJSAPILightWeightSet(GateRef obj) return Int32Equal(objectType, Int32(static_cast(JSType::JS_API_LIGHT_WEIGHT_SET))); } +inline GateRef StubBuilder::IsLinkedNode(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::LINKED_NODE))); +} + +inline GateRef StubBuilder::IsJSAPIHashMap(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::JS_API_HASH_MAP))); +} + +inline GateRef StubBuilder::IsJSAPIHashSet(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::JS_API_HASH_SET))); +} + +inline GateRef StubBuilder::IsJSAPILinkedList(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::JS_API_LINKED_LIST))); +} + +inline GateRef StubBuilder::IsJSAPIList(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::JS_API_LIST))); +} + inline GateRef StubBuilder::IsJSAPIArrayList(GateRef obj) { GateRef objectType = GetObjectType(LoadHClass(obj)); diff --git a/ecmascript/compiler/stub_builder.h b/ecmascript/compiler/stub_builder.h index 410860b6d1..4bb6a1b088 100644 --- a/ecmascript/compiler/stub_builder.h +++ b/ecmascript/compiler/stub_builder.h @@ -261,8 +261,13 @@ public: GateRef IsJSAPIPlainArray(GateRef obj); GateRef IsJSAPIQueue(GateRef obj); GateRef IsJSAPIDeque(GateRef obj); - GateRef IsJSAPILightWeightMap(GateRef attr); - GateRef IsJSAPILightWeightSet(GateRef attr); + GateRef IsJSAPILightWeightMap(GateRef obj); + GateRef IsJSAPILightWeightSet(GateRef obj); + GateRef IsLinkedNode(GateRef obj); + GateRef IsJSAPIHashMap(GateRef obj); + GateRef IsJSAPIHashSet(GateRef obj); + GateRef IsJSAPILinkedList(GateRef obj); + GateRef IsJSAPIList(GateRef obj); GateRef IsJSAPIArrayList(GateRef obj); GateRef GetTarget(GateRef proxyObj); GateRef HandlerBaseIsAccessor(GateRef attr); diff --git a/ecmascript/containers/containers_hashset.cpp b/ecmascript/containers/containers_hashset.cpp index d09c6ea401..c7f150b85a 100644 --- a/ecmascript/containers/containers_hashset.cpp +++ b/ecmascript/containers/containers_hashset.cpp @@ -269,9 +269,9 @@ JSTaggedValue ContainersHashSet::ForEach(EcmaRuntimeCallInfo *argv) currentKey.Update(node->GetKey()); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, - thisArgHandle, undefined, 2); // 2: two args + thisArgHandle, undefined, 3); // 3: three args RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - info->SetCallArg(currentKey.GetTaggedValue(), thisHandle.GetTaggedValue()); + info->SetCallArg(currentKey.GetTaggedValue(), currentKey.GetTaggedValue(), thisHandle.GetTaggedValue()); JSTaggedValue funcResult = JSFunction::Call(info); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); } diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp index 502172edab..54762c9df4 100644 --- a/ecmascript/containers/containers_private.cpp +++ b/ecmascript/containers/containers_private.cpp @@ -980,7 +980,8 @@ JSHandle ContainersPrivate::InitializeList(JSThread *thread) 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, "forEach", ContainersList::ForEach, FuncLength::ONE, + static_cast(BUILTINS_STUB_ID(ListForEach))); SetFrozenFunction(thread, listFuncPrototype, "replaceAllElements", ContainersList::ReplaceAllElements, FuncLength::ONE); SetFrozenFunction(thread, listFuncPrototype, "equal", ContainersList::Equal, FuncLength::ONE); @@ -1042,7 +1043,8 @@ JSHandle ContainersPrivate::InitializeLinkedList(JSThread *thread SetFrozenFunction(thread, linkedListFuncPrototype, "convertToArray", ContainersLinkedList::ConvertToArray, FuncLength::ONE); SetFrozenFunction(thread, linkedListFuncPrototype, "set", ContainersLinkedList::Set, FuncLength::ONE); - SetFrozenFunction(thread, linkedListFuncPrototype, "forEach", ContainersLinkedList::ForEach, FuncLength::ONE); + SetFrozenFunction(thread, linkedListFuncPrototype, "forEach", ContainersLinkedList::ForEach, FuncLength::ONE, + static_cast(BUILTINS_STUB_ID(LinkedListForEach))); JSHandle lengthGetter = CreateGetter(thread, ContainersLinkedList::Length, "length", FuncLength::ZERO); @@ -1114,7 +1116,8 @@ JSHandle ContainersPrivate::InitializeHashMap(JSThread *thread) // HashMap.prototype.get() SetFrozenFunction(thread, hashMapFuncPrototype, "get", ContainersHashMap::Get, FuncLength::ONE); // HashMap.prototype.forEach() - SetFrozenFunction(thread, hashMapFuncPrototype, "forEach", ContainersHashMap::ForEach, FuncLength::TWO); + SetFrozenFunction(thread, hashMapFuncPrototype, "forEach", ContainersHashMap::ForEach, FuncLength::TWO, + static_cast(BUILTINS_STUB_ID(HashMapForEach))); // HashMap.prototype.hasKey() SetFrozenFunction(thread, hashMapFuncPrototype, "hasKey", ContainersHashMap::HasKey, FuncLength::ONE); // HashMap.prototype.hasValue() @@ -1190,7 +1193,8 @@ JSHandle ContainersPrivate::InitializeHashSet(JSThread *thread) SetFrozenFunction(thread, hashSetFuncPrototype, "clear", ContainersHashSet::Clear, FuncLength::ZERO); SetFrozenFunction(thread, hashSetFuncPrototype, "values", ContainersHashSet::Values, FuncLength::ZERO); SetFrozenFunction(thread, hashSetFuncPrototype, "entries", ContainersHashSet::Entries, FuncLength::ZERO); - SetFrozenFunction(thread, hashSetFuncPrototype, "forEach", ContainersHashSet::ForEach, FuncLength::TWO); + SetFrozenFunction(thread, hashSetFuncPrototype, "forEach", ContainersHashSet::ForEach, FuncLength::TWO, + static_cast(BUILTINS_STUB_ID(HashSetForEach))); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); // @@ToStringTag diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index cb82c7e5ad..a6e5c0e12f 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -18,6 +18,7 @@ #include "ecmascript/accessor_data.h" #include "ecmascript/base/number_helper.h" #include "ecmascript/base/string_helper.h" +#include "ecmascript/compiler/builtins/containers_stub_builder.h" #include "ecmascript/compiler/call_signature.h" #include "ecmascript/compiler/ecma_opcode_des.h" #include "ecmascript/compiler/rt_call_signature.h" @@ -44,6 +45,7 @@ #include "ecmascript/message_string.h" #include "ecmascript/object_factory.h" #include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_node.h" #include "ecmascript/ts_types/ts_manager.h" #include "libpandafile/bytecode_instruction-inl.h" @@ -2055,6 +2057,44 @@ DEF_RUNTIME_STUBS(LdObjByName) return RuntimeLdObjByName(thread, receiver, propKey, callGetter, undefined).GetRawData(); } +DEF_RUNTIME_STUBS(ContainerRBTreeForEach) +{ + RUNTIME_STUBS_HEADER(ContainerRBTreeForEach); + JSHandle node = GetHArg(argv, argc, 0); // 0: param index + JSHandle callbackFnHandle = GetHArg(argv, argc, 1); // 1: param index + JSHandle thisArgHandle = GetHArg(argv, argc, 2); // 2: param index + JSHandle thisHandle = GetHArg(argv, argc, 3); // 3: param index + JSHandle type = GetHArg(argv, argc, 4); // 4: param index + + ASSERT(node->IsRBTreeNode()); + ASSERT(callbackFnHandle->IsCallable()); + ASSERT(type->IsInt()); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + auto containersType = static_cast(type->GetInt()); + JSMutableHandle queue(thread, thread->GetEcmaVM()->GetFactory()->NewTaggedQueue(0)); + JSMutableHandle treeNode(thread, JSTaggedValue::Undefined()); + queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, node))); + while (!queue->Empty()) { + treeNode.Update(queue->Pop(thread)); + EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, + undefined, 3); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); + info->SetCallArg(containersType == kungfu::ContainersType::HASHSET_FOREACH ? + treeNode->GetKey() : treeNode->GetValue(), treeNode->GetKey(), thisHandle.GetTaggedValue()); + JSTaggedValue funcResult = JSFunction::Call(info); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult.GetRawData()); + if (!treeNode->GetLeft().IsHole()) { + JSHandle left(thread, treeNode->GetLeft()); + queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, left))); + } + if (!treeNode->GetRight().IsHole()) { + JSHandle right(thread, treeNode->GetRight()); + queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, right))); + } + } + return JSTaggedValue::True().GetRawData(); +} + JSTaggedType RuntimeStubs::CreateArrayFromList([[maybe_unused]]uintptr_t argGlue, int32_t argc, JSTaggedValue *argvPtr) { auto thread = JSThread::GlueToJSThread(argGlue); diff --git a/ecmascript/stubs/runtime_stubs.h b/ecmascript/stubs/runtime_stubs.h index ded62fabbb..1af240f88f 100644 --- a/ecmascript/stubs/runtime_stubs.h +++ b/ecmascript/stubs/runtime_stubs.h @@ -292,7 +292,8 @@ using JSFunctionReentry = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, const V(LdPatchVar) \ V(StPatchVar) \ V(LdObjByName) \ - V(DeoptHandler) + V(DeoptHandler) \ + V(ContainerRBTreeForEach) #define RUNTIME_STUB_LIST(V) \ RUNTIME_ASM_STUB_LIST(V) \ diff --git a/test/moduletest/container/container_hashmap.js b/test/moduletest/container/container_hashmap.js index 96dbe176a3..19730b877c 100644 --- a/test/moduletest/container/container_hashmap.js +++ b/test/moduletest/container/container_hashmap.js @@ -165,6 +165,31 @@ if (globalThis["ArkPrivate"] != undefined) { } } res.forEach(elements); + + // test RBTree + let collisionMap = new fastmap(); + let count = 0; + // same hash when mod 1024 + collisionMap.set(1224, 1); + collisionMap.set(1285, 2); + collisionMap.set(1463, 3); + collisionMap.set(4307, 4); + collisionMap.set(5135, 5); + collisionMap.set(5903, 6); + collisionMap.set(6603, 7); + collisionMap.set(6780, 8); + collisionMap.set(8416, 9); + collisionMap.set(9401, 10); + collisionMap.set(9740, 11); + collisionMap.forEach((value, key, hashMap) => { + if (hashMap.get(key) == value) { + count += value; + } + }); + if (count != 66) { // 66: 1 + 2 + 3 + ... + 11 + print("test RBTree forEach fail. count=" + count); + } + if (!flag) { print("Test HashMap success!!!"); } else {