1 /*
2 * Copyright (c) 2021 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_iterator.h"
17
18 #include "ecmascript/accessor_data.h"
19 #include "ecmascript/ecma_macros.h"
20 #include "ecmascript/ecma_vm.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/interpreter/interpreter.h"
23 #include "ecmascript/js_symbol.h"
24 #include "ecmascript/js_async_from_sync_iterator.h"
25 #include "ecmascript/object_factory.h"
26 #include "ecmascript/object_fast_operator-inl.h"
27
28 namespace panda::ecmascript {
IteratorCloseAndReturn(JSThread * thread,const JSHandle<JSTaggedValue> & iter)29 JSTaggedValue JSIterator::IteratorCloseAndReturn(JSThread *thread, const JSHandle<JSTaggedValue> &iter)
30 {
31 ASSERT(thread->HasPendingException());
32 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33 JSTaggedValue exception = thread->GetException();
34 JSHandle<JSTaggedValue> record = JSHandle<JSTaggedValue>(factory->NewCompletionRecord(CompletionRecordType::THROW,
35 JSHandle<JSTaggedValue>(thread, exception)));
36 JSHandle<JSTaggedValue> result = JSIterator::IteratorClose(thread, iter, record);
37 if (result->IsCompletionRecord()) {
38 return CompletionRecord::Cast(result->GetTaggedObject())->GetValue();
39 }
40 return result.GetTaggedValue();
41 }
42
GetIterator(JSThread * thread,const JSHandle<JSTaggedValue> & obj)43 JSHandle<JSTaggedValue> JSIterator::GetIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
44 {
45 // 1.ReturnIfAbrupt(obj).
46 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj);
47 // 2.If method was not passed, then
48 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
49 JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol();
50 JSHandle<JSTaggedValue> func = JSObject::GetMethod(thread, obj, iteratorSymbol);
51 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj);
52
53 return GetIterator(thread, obj, func);
54 }
55
GetIterator(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & method)56 JSHandle<JSTaggedValue> JSIterator::GetIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
57 const JSHandle<JSTaggedValue> &method)
58 {
59 // 1.ReturnIfAbrupt(obj).
60 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj);
61 // 3.Let iterator be Call(method,obj).
62 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
63 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, obj, undefined, 0);
64 JSTaggedValue ret = JSFunction::Call(info);
65 JSHandle<JSTaggedValue> iter(thread, ret);
66 // 4.ReturnIfAbrupt(iterator).
67 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter);
68 // 5.If Type(iterator) is not Object, throw a TypeError exception
69 if (!iter->IsECMAObject()) {
70 THROW_TYPE_ERROR_AND_RETURN(thread, "", undefined);
71 }
72 return iter;
73 }
74
GetAsyncIterator(JSThread * thread,const JSHandle<JSTaggedValue> & obj)75 JSHandle<JSTaggedValue> JSIterator::GetAsyncIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
76 {
77 // 3.If method is not present, then
78 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
79 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
80 JSHandle<JSTaggedValue> asynciteratorSymbol = env->GetAsyncIteratorSymbol();
81 // i. Set method to ? GetMethod(obj, @@asyncIterator).
82 // ii. If method is undefined, then
83 JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, obj, asynciteratorSymbol);
84 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj);
85 if (method->IsUndefined()) {
86 JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol();
87 JSHandle<JSTaggedValue> func = JSObject::GetMethod(thread, obj, iteratorSymbol);
88 JSHandle<JSTaggedValue> syncIterator = GetIterator(thread, obj, func);
89 JSHandle<JSTaggedValue> nextStr = thread->GlobalConstants()->GetHandledNextString();
90 JSHandle<JSTaggedValue> nextMethod = JSTaggedValue::GetProperty(thread, syncIterator, nextStr).GetValue();
91 JSHandle<AsyncIteratorRecord> syncIteratorRecord =
92 factory->NewAsyncIteratorRecord(syncIterator, nextMethod, false);
93 JSHandle<JSTaggedValue> asyncIterator =
94 JSAsyncFromSyncIterator::CreateAsyncFromSyncIterator(thread, syncIteratorRecord);
95 return asyncIterator;
96 }
97
98 // 4.Let iterator be Call(method,obj).
99 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
100 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, obj, undefined, 0);
101 JSTaggedValue ret = JSFunction::Call(info);
102 JSHandle<JSTaggedValue> iterator(thread, ret);
103 // 5.If Type(iterator) is not Object, throw a TypeError exception
104 if (!iterator->IsECMAObject()) {
105 THROW_TYPE_ERROR_AND_RETURN(thread, "", undefined);
106 }
107 return iterator;
108 }
109
110
111 // 7.4.2
IteratorNext(JSThread * thread,const JSHandle<JSTaggedValue> & iter)112 JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<JSTaggedValue> &iter)
113 {
114 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
115
116 // 1.If value was not passed, then Let result be Invoke(iterator, "next", « »).
117 JSHandle<JSTaggedValue> key(globalConst->GetHandledNextString());
118 JSHandle<JSTaggedValue> next(JSObject::GetMethod(thread, iter, key));
119 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
120 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iter, undefined, 0);
121 JSTaggedValue ret = JSFunction::Call(info);
122 // 3.ReturnIfAbrupt(result)
123 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined);
124 // 4.If Type(result) is not Object, throw a TypeError exception.
125 if (!ret.IsECMAObject()) {
126 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined);
127 }
128 JSHandle<JSTaggedValue> result(thread, ret);
129 return result;
130 }
131
IteratorNext(JSThread * thread,const JSHandle<JSTaggedValue> & iter,const JSHandle<JSTaggedValue> & value)132 JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<JSTaggedValue> &iter,
133 const JSHandle<JSTaggedValue> &value)
134 {
135 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
136 // 2.Let result be Invoke(iterator, "next", «value»).
137 JSHandle<JSTaggedValue> key(globalConst->GetHandledNextString());
138 JSHandle<JSTaggedValue> next(JSObject::GetMethod(thread, iter, key));
139 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
140 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iter, undefined, 1);
141 info->SetCallArg(value.GetTaggedValue());
142 JSTaggedValue ret = JSFunction::Call(info);
143 // 3.ReturnIfAbrupt(result)
144 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined);
145 // 4.If Type(result) is not Object, throw a TypeError exception.
146 if (!ret.IsECMAObject()) {
147 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined);
148 }
149 JSHandle<JSTaggedValue> result(thread, ret);
150 return result;
151 }
152
IteratorNext(JSThread * thread,const JSHandle<AsyncIteratorRecord> & iter,const JSHandle<JSTaggedValue> & value)153 JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<AsyncIteratorRecord> &iter,
154 const JSHandle<JSTaggedValue> &value)
155 {
156 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
157 // 2.Let result be Invoke(iterator, "next", «value»).
158 JSHandle<JSTaggedValue> iterator(thread, iter->GetIterator());
159 JSHandle<JSTaggedValue> next(thread, iter->GetNextMethod());
160 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
161 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iterator, undefined, 1);
162 info->SetCallArg(value.GetTaggedValue());
163 JSTaggedValue ret = JSFunction::Call(info);
164 // 3.ReturnIfAbrupt(result)
165 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined);
166 // 4.If Type(result) is not Object, throw a TypeError exception.
167 if (!ret.IsECMAObject()) {
168 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined);
169 }
170 JSHandle<JSTaggedValue> result(thread, ret);
171 return result;
172 }
173
IteratorNext(JSThread * thread,const JSHandle<AsyncIteratorRecord> & iter)174 JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<AsyncIteratorRecord> &iter)
175 {
176 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
177 // 2.Let result be Invoke(iterator, "next", «value»).
178 JSHandle<JSTaggedValue> iterator(thread, iter->GetIterator());
179 JSHandle<JSTaggedValue> next(thread, iter->GetNextMethod());
180 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
181 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iterator, undefined, 0);
182 JSTaggedValue ret = JSFunction::Call(info);
183 // 3.ReturnIfAbrupt(result)
184 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined);
185 // 4.If Type(result) is not Object, throw a TypeError exception.
186 if (!ret.IsECMAObject()) {
187 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined);
188 }
189 JSHandle<JSTaggedValue> result(thread, ret);
190 return result;
191 }
192 // 7.4.3
IteratorComplete(JSThread * thread,const JSHandle<JSTaggedValue> & iterResult)193 bool JSIterator::IteratorComplete(JSThread *thread, const JSHandle<JSTaggedValue> &iterResult)
194 {
195 ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject");
196 // Return ToBoolean(Get(iterResult, "done")).
197 JSHandle<JSTaggedValue> doneStr = thread->GlobalConstants()->GetHandledDoneString();
198 JSHandle<JSTaggedValue> done = JSTaggedValue::GetProperty(thread, iterResult, doneStr).GetValue();
199 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
200 return done->ToBoolean();
201 }
202 // 7.4.4
IteratorValue(JSThread * thread,const JSHandle<JSTaggedValue> & iterResult)203 JSHandle<JSTaggedValue> JSIterator::IteratorValue(JSThread *thread, const JSHandle<JSTaggedValue> &iterResult)
204 {
205 ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject");
206 // Return Get(iterResult, "value").
207 JSHandle<JSTaggedValue> valueStr = thread->GlobalConstants()->GetHandledValueString();
208 JSHandle<JSTaggedValue> value = JSTaggedValue::GetProperty(thread, iterResult, valueStr).GetValue();
209 return value;
210 }
211 // 7.4.5
IteratorStep(JSThread * thread,const JSHandle<JSTaggedValue> & iter)212 JSHandle<JSTaggedValue> JSIterator::IteratorStep(JSThread *thread, const JSHandle<JSTaggedValue> &iter)
213 {
214 // 1.Let result be IteratorNext(iterator).
215 JSHandle<JSTaggedValue> result = IteratorNext(thread, iter);
216 // 2.ReturnIfAbrupt(result).
217 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result);
218 // 3.Let done be IteratorComplete(result).
219 bool done = IteratorComplete(thread, result);
220 // 4.ReturnIfAbrupt(done).
221 JSHandle<JSTaggedValue> doneHandle(thread, JSTaggedValue(done));
222 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, doneHandle);
223 // 5.If done is true, return false.
224 if (done) {
225 JSHandle<JSTaggedValue> falseHandle(thread, JSTaggedValue::False());
226 return falseHandle;
227 }
228 return result;
229 }
230 // 7.4.6
IteratorClose(JSThread * thread,const JSHandle<JSTaggedValue> & iter,const JSHandle<JSTaggedValue> & completion)231 JSHandle<JSTaggedValue> JSIterator::IteratorClose(JSThread *thread, const JSHandle<JSTaggedValue> &iter,
232 const JSHandle<JSTaggedValue> &completion)
233 {
234 // 1.Assert: Type(iterator) is Object.
235 ASSERT_PRINT(iter->IsECMAObject(), "iter must be JSObject");
236 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
237 JSHandle<JSTaggedValue> exceptionOnThread;
238 if (thread->HasPendingException()) {
239 exceptionOnThread = JSHandle<JSTaggedValue>(thread, thread->GetException());
240 thread->ClearException();
241 }
242 JSTaggedValue returnStr = globalConst->GetReturnString();
243 // 3.Let return be GetMethod(iterator, "return").
244 JSTaggedValue func = ObjectFastOperator::FastGetPropertyByName(thread, iter.GetTaggedValue(), returnStr);
245 // 4.ReturnIfAbrupt(return).
246 JSHandle<JSTaggedValue> returnFunc(thread, func);
247 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, returnFunc);
248
249 // 5.If return is undefined, return Completion(completion).
250 if (returnFunc->IsUndefined() || returnFunc->IsNull()) {
251 if (!exceptionOnThread.IsEmpty()) {
252 thread->SetException(exceptionOnThread.GetTaggedValue());
253 }
254 return completion;
255 }
256
257 if (!returnFunc->IsCallable()) {
258 THROW_TYPE_ERROR_AND_RETURN(thread, "return function is not Callable", returnFunc);
259 }
260 // 6.Let innerResult be Call(return, iterator, « »).
261 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
262 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, returnFunc, iter, undefined, 0);
263 JSTaggedValue ret = JSFunction::Call(info);
264 if (!exceptionOnThread.IsEmpty()) {
265 thread->SetException(exceptionOnThread.GetTaggedValue());
266 }
267 JSHandle<JSTaggedValue> innerResult(thread, ret);
268 JSHandle<CompletionRecord> completionRecord(thread, globalConst->GetUndefined());
269 if (completion->IsCompletionRecord()) {
270 completionRecord = JSHandle<CompletionRecord>::Cast(completion);
271 }
272 // 7.If completion.[[type]] is throw, return Completion(completion).
273 if (!completionRecord.GetTaggedValue().IsUndefined() && completionRecord->IsThrow()) {
274 if (!exceptionOnThread.IsEmpty()) {
275 thread->SetException(exceptionOnThread.GetTaggedValue());
276 }
277 return completion;
278 }
279 // 8.If innerResult.[[type]] is throw, return Completion(innerResult).
280 if (thread->HasPendingException()) {
281 return innerResult;
282 }
283 // 9.If Type(innerResult.[[value]]) is not Object, throw a TypeError exception.
284 if (!innerResult->IsECMAObject()) {
285 THROW_TYPE_ERROR_AND_RETURN(thread, "", undefined);
286 }
287 if (!exceptionOnThread.IsEmpty()) {
288 thread->SetException(exceptionOnThread.GetTaggedValue());
289 }
290 return completion;
291 }
292 // 7.4.7
CreateIterResultObject(JSThread * thread,const JSHandle<JSTaggedValue> & value,bool done)293 JSHandle<JSObject> JSIterator::CreateIterResultObject(JSThread *thread, const JSHandle<JSTaggedValue> &value, bool done)
294 {
295 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
296 auto globalConst = thread->GlobalConstants();
297 // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%).
298 JSHandle<JSHClass> klass = JSHandle<JSHClass>::Cast(globalConst->GetHandledIteratorResultClass());
299 JSHandle<JSObject> obj = factory->NewJSObject(klass);
300
301 // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value).
302 // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done).
303 obj->SetPropertyInlinedProps(thread, VALUE_INLINE_PROPERTY_INDEX, value.GetTaggedValue());
304 obj->SetPropertyInlinedProps(thread, DONE_INLINE_PROPERTY_INDEX, JSTaggedValue(done));
305 // 5. Return obj.
306 return obj;
307 }
308 } // namespace panda::ecmascript
309