1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/js_api/js_api_hashmap_iterator.h"
17
18 #include "ecmascript/containers/containers_errors.h"
19
20 namespace panda::ecmascript {
21 using BuiltinsBase = base::BuiltinsBase;
22 using ContainerError = containers::ContainerError;
23 using ErrorFlag = containers::ErrorFlag;
Next(EcmaRuntimeCallInfo * argv)24 JSTaggedValue JSAPIHashMapIterator::Next(EcmaRuntimeCallInfo *argv)
25 {
26 ASSERT(argv);
27 JSThread *thread = argv->GetThread();
28 [[maybe_unused]] EcmaHandleScope handleScope(thread);
29 JSHandle<JSTaggedValue> input(BuiltinsBase::GetThis(argv));
30
31 if (!input->IsJSAPIHashMapIterator()) {
32 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
33 "The Symbol.iterator method cannot be bound");
34 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
35 }
36 JSHandle<JSAPIHashMapIterator> iter = JSHandle<JSAPIHashMapIterator>::Cast(input);
37 JSHandle<JSTaggedValue> iteratedHashMap(thread, iter->GetIteratedHashMap());
38 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
39 // If m is undefined, return undefinedIteratorResult.
40 if (iteratedHashMap->IsUndefined()) {
41 return globalConst->GetUndefinedIterResult();
42 }
43 JSHandle<TaggedHashArray> tableArr(thread, JSHandle<JSAPIHashMap>::Cast(iteratedHashMap)->GetTable());
44 uint32_t tableLength = tableArr->GetLength();
45 uint32_t index = iter->GetNextIndex();
46
47 JSMutableHandle<TaggedQueue> queue(thread, iter->GetTaggedQueue());
48 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
49 JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
50 JSMutableHandle<TaggedNode> currentNode(thread, JSTaggedValue::Undefined());
51
52 IterationKind itemKind = iter->GetIterationKind();
53 while (index < tableLength) {
54 currentNode.Update(JSAPIHashMapIterator::FastGetCurrentNode(thread, iter, queue, tableArr));
55 if (!currentNode.GetTaggedValue().IsHole() && !currentNode.GetTaggedValue().IsUndefined()) {
56 JSTaggedValue key = currentNode->GetKey();
57 keyHandle.Update(key);
58 if (itemKind == IterationKind::KEY) {
59 return JSIterator::CreateIterResultObject(thread, keyHandle, false).GetTaggedValue();
60 }
61 valueHandle.Update(currentNode->GetValue());
62 if (itemKind == IterationKind::VALUE) {
63 return JSIterator::CreateIterResultObject(thread, valueHandle, false).GetTaggedValue();
64 }
65 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
66 JSHandle<TaggedArray> array = factory->NewTaggedArray(2); // 2 means the length of array
67 array->Set(thread, 0, keyHandle);
68 array->Set(thread, 1, valueHandle);
69 JSHandle<JSTaggedValue> keyAndValue(JSArray::CreateArrayFromList(thread, array));
70 return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue();
71 }
72 index++;
73 if (!currentNode.GetTaggedValue().IsRBTreeNode()) {
74 iter->SetNextIndex(index);
75 }
76 }
77 // Set [[IteratedMap]] to undefined.
78 iter->SetIteratedHashMap(thread, JSTaggedValue::Undefined());
79 return globalConst->GetUndefinedIterResult();
80 }
81
FastGetCurrentNode(JSThread * thread,JSHandle<JSAPIHashMapIterator> & iter,JSMutableHandle<TaggedQueue> & queue,JSHandle<TaggedHashArray> & tableArr)82 JSHandle<JSTaggedValue> JSAPIHashMapIterator::FastGetCurrentNode(JSThread *thread,
83 JSHandle<JSAPIHashMapIterator> &iter,
84 JSMutableHandle<TaggedQueue> &queue,
85 JSHandle<TaggedHashArray> &tableArr)
86 {
87 JSHandle<JSTaggedValue> rootValue(thread, JSTaggedValue::Undefined());
88 uint32_t index = iter->GetNextIndex();
89 JSHandle<JSTaggedValue> prevNodeValue(thread, iter->GetCurrentNodeResult());
90 if (prevNodeValue->IsRBTreeNode()) {
91 return GetCurrentNode(thread, iter, queue, tableArr);
92 }
93 if (prevNodeValue->IsUndefined() || prevNodeValue->IsHole()) {
94 rootValue = JSHandle<JSTaggedValue>(thread, tableArr->Get(index));
95 iter->SetCurrentNodeResult(thread, rootValue);
96 return rootValue;
97 }
98 JSHandle<LinkedNode> prevNode = JSHandle<LinkedNode>::Cast(prevNodeValue);
99 if (!prevNode->GetNext().IsHole()) {
100 JSHandle<JSTaggedValue> next(thread, prevNode->GetNext());
101 iter->SetCurrentNodeResult(thread, next);
102 return next;
103 }
104 iter->SetCurrentNodeResult(thread, JSTaggedValue::Undefined());
105 return rootValue;
106 }
107
108 // level traversal
GetCurrentNode(JSThread * thread,JSHandle<JSAPIHashMapIterator> & iter,JSMutableHandle<TaggedQueue> & queue,JSHandle<TaggedHashArray> & tableArr)109 JSHandle<JSTaggedValue> JSAPIHashMapIterator::GetCurrentNode(JSThread *thread,
110 JSHandle<JSAPIHashMapIterator> &iter,
111 JSMutableHandle<TaggedQueue> &queue,
112 JSHandle<TaggedHashArray> &tableArr)
113 {
114 JSHandle<JSTaggedValue> rootValue(thread, JSTaggedValue::Undefined());
115 uint32_t index = iter->GetNextIndex();
116 if (queue->Empty()) {
117 rootValue = JSHandle<JSTaggedValue>(thread, tableArr->Get(index));
118 if (rootValue->IsHole()) {
119 iter->SetNextIndex(++index);
120 return rootValue;
121 }
122 } else {
123 rootValue = JSHandle<JSTaggedValue>(thread, queue->Pop(thread));
124 }
125 if (rootValue->IsRBTreeNode()) {
126 JSHandle<RBTreeNode> root = JSHandle<RBTreeNode>::Cast(rootValue);
127 if (!root->GetLeft().IsHole()) {
128 JSHandle<JSTaggedValue> left(thread, root->GetLeft());
129 queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, left)));
130 }
131 if (!root->GetRight().IsHole()) {
132 JSHandle<JSTaggedValue> right(thread, root->GetRight());
133 queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, right)));
134 }
135 }
136 iter->SetTaggedQueue(thread, queue.GetTaggedValue());
137 if (queue->Empty()) {
138 iter->SetNextIndex(++index);
139 }
140 return rootValue;
141 }
142
CreateHashMapIterator(JSThread * thread,const JSHandle<JSTaggedValue> & obj,IterationKind kind)143 JSHandle<JSTaggedValue> JSAPIHashMapIterator::CreateHashMapIterator(JSThread *thread,
144 const JSHandle<JSTaggedValue> &obj,
145 IterationKind kind)
146 {
147 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
148 if (!obj->IsJSAPIHashMap()) {
149 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR,
150 "The Symbol.iterator method cannot be bound");
151 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
152 }
153 JSHandle<JSTaggedValue> iter(factory->NewJSAPIHashMapIterator(JSHandle<JSAPIHashMap>(obj), kind));
154 return iter;
155 }
156 } // namespace panda::ecmascript