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_async_generator_object.h"
17 #include "ecmascript/async_generator_helper.h"
18 #include "ecmascript/accessor_data.h"
19 #include "ecmascript/base/builtins_base.h"
20 #include "ecmascript/builtins/builtins_promise.h"
21 #include "ecmascript/ecma_string.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/interpreter/interpreter.h"
24 #include "ecmascript/js_promise.h"
25 #include "ecmascript/object_factory.h"
26 #include "ecmascript/tagged_queue.h"
27 #include "ecmascript/generator_helper.h"
28 #include "ecmascript/js_iterator.h"
29 #include "ecmascript/js_tagged_value-inl.h"
30 #include "ecmascript/js_function.h"
31
32
33 namespace panda::ecmascript {
34 using BuiltinsPromise = builtins::BuiltinsPromise;
35
36 // AsyncGeneratorValidate ( generator, generatorBrand )
AsyncGeneratorValidate(JSThread * thread,const JSHandle<JSTaggedValue> & gen,const JSTaggedValue & val)37 void JSAsyncGeneratorObject::AsyncGeneratorValidate(JSThread *thread, const JSHandle<JSTaggedValue> &gen,
38 const JSTaggedValue &val)
39 {
40 // 1. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorContext]]).
41 // 2. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorState]]).
42 // 3. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorQueue]]).
43 if (!gen->IsAsyncGeneratorObject()) {
44 THROW_TYPE_ERROR(thread, "Not a asyncgenerator object");
45 }
46 // 4. If generator.[[GeneratorBrand]] is not the same value as generatorBrand, throw a TypeError exception.
47 JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, gen);
48 RETURN_IF_ABRUPT_COMPLETION(thread);
49 JSHandle<JSAsyncGeneratorObject> generator = JSHandle<JSAsyncGeneratorObject>::Cast(obj);
50 if (!JSTaggedValue::SameValue(generator->GetGeneratorBrand(), val)) {
51 THROW_TYPE_ERROR(thread, "Results are not equal");
52 }
53 }
54
AsyncGeneratorResolve(JSThread * thread,const JSHandle<JSAsyncGeneratorObject> & generator,const JSHandle<JSTaggedValue> value,bool done)55 JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorResolve(JSThread *thread,
56 const JSHandle<JSAsyncGeneratorObject> &generator,
57 const JSHandle<JSTaggedValue> value, bool done)
58 {
59 // 1. Assert: generator is an AsyncGenerator instance.
60 ASSERT(generator->IsAsyncGeneratorObject());
61 // 2. Let queue be generator.[[AsyncGeneratorQueue]].
62 JSHandle<TaggedQueue> queue(thread, generator->GetAsyncGeneratorQueue());
63 // 3. Assert: queue is not an empty List.
64 ASSERT(!(queue->Empty()));
65 // 4. Let next be the first element of queue.
66 JSHandle<AsyncGeneratorRequest> next(thread, queue->Front());
67 // 5. Remove the first element from queue.
68 queue->Pop(thread);
69 // 6. Let promiseCapability be next.[[Capability]].
70 JSHandle<PromiseCapability> capability(thread, next->GetCapability());
71 // 7. Let iteratorResult be ! CreateIterResultObject(value, done).
72 JSHandle<JSObject> iteratorResult = JSIterator::CreateIterResultObject(thread, value, done);
73 // 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
74 JSHandle<JSTaggedValue> its = JSHandle<JSTaggedValue>::Cast(iteratorResult);
75 JSHandle<JSTaggedValue> resolve(thread, capability->GetResolve());
76 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
77 EcmaRuntimeCallInfo* info =
78 EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1);
79 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
80 info->SetCallArg(its.GetTaggedValue());
81 [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
82 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
83
84 // 9. Perform ! AsyncGeneratorResumeNext(generator).
85 AsyncGeneratorResumeNext(thread, generator);
86 // 10. Return undefined.
87 return JSTaggedValue::Undefined();
88 }
89
AsyncGeneratorReject(JSThread * thread,const JSHandle<JSAsyncGeneratorObject> & generator,const JSHandle<JSTaggedValue> value)90 JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorReject(JSThread *thread,
91 const JSHandle<JSAsyncGeneratorObject> &generator,
92 const JSHandle<JSTaggedValue> value)
93 {
94 // 1. Assert: generator is an AsyncGenerator instance.
95 ASSERT(generator->IsAsyncGeneratorObject());
96 // 2. Let queue be generator.[[AsyncGeneratorQueue]].
97 JSHandle<TaggedQueue> queue(thread, generator->GetAsyncGeneratorQueue());
98 // 3. Assert: queue is not an empty List.
99 ASSERT(!(queue->Empty()));
100 // 4. Let next be the first element of queue.
101 JSHandle<JSTaggedValue> val(thread, queue->Front());
102 JSHandle<AsyncGeneratorRequest> next = JSHandle<AsyncGeneratorRequest>::Cast(val);
103 // 5. Remove the first element from queue.
104 queue->Pop(thread);
105 // 6. Let promiseCapability be next.[[Capability]].
106 JSHandle<PromiseCapability> capability(thread, next->GetCapability());
107 // 7. Perform ! Call(promiseCapability.[[Reject]], undefined, ? exception ?).
108 JSHandle<JSTaggedValue> reject(thread, capability->GetReject());
109 const GlobalEnvConstants *constants = thread->GlobalConstants();
110 const JSHandle<JSTaggedValue> thisArg = constants->GetHandledUndefined();
111 const JSHandle<JSTaggedValue> undefined = constants->GetHandledUndefined();
112 EcmaRuntimeCallInfo* info =
113 EcmaInterpreter::NewRuntimeCallInfo(thread, reject, thisArg, undefined, 1);
114 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
115 info->SetCallArg(value.GetTaggedValue());
116 [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
117 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
118 // 8. Perform ! AsyncGeneratorResumeNext(generator).
119 AsyncGeneratorResumeNext(thread, generator);
120 // 9. Return undefined.
121 return JSTaggedValue::Undefined();
122 }
123
AsyncGeneratorResumeNext(JSThread * thread,const JSHandle<JSAsyncGeneratorObject> & generator)124 JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorResumeNext(JSThread *thread,
125 const JSHandle<JSAsyncGeneratorObject> &generator)
126 {
127 // 1. Assert: generator is an AsyncGenerator instance.
128 ASSERT(generator->IsAsyncGeneratorObject());
129 // 2. Let state be generator.[[AsyncGeneratorState]].
130 JSAsyncGeneratorState state = generator->GetAsyncGeneratorState();
131 // 3. Assert: state is not executing.
132 ASSERT(state != JSAsyncGeneratorState::EXECUTING);
133 // 4. If state is awaiting-return, return undefined.
134 if (state == JSAsyncGeneratorState::AWAITING_RETURN) {
135 return JSTaggedValue::Undefined();
136 }
137 // 5. Let queue be generator.[[AsyncGeneratorQueue]].
138 JSHandle<TaggedQueue> queue(thread, generator->GetAsyncGeneratorQueue());
139 // 6. If queue is an empty List, return undefined.
140 if (queue->Empty()) {
141 return JSTaggedValue::Undefined();
142 }
143 // 7. Let next be the value of the first element of queue.
144 JSHandle<AsyncGeneratorRequest> next(thread, queue->Front());
145 // 8. Assert: next is an AsyncGeneratorRequest record.
146 ASSERT(next->GetClass()->IsAsyncGeneratorRequest());
147 // 9. Let completion be next.[[Completion]].
148 JSTaggedValue rcd = next->GetCompletion();
149 JSHandle<CompletionRecord> completion(thread, rcd);
150 CompletionRecordType type = completion->GetType();
151 // 10. If completion is an abrupt completion, then
152 if (thread->HasPendingException() || type != CompletionRecordType::NORMAL) {
153 // a. If state is suspendedStart, then
154 if (state == JSAsyncGeneratorState::SUSPENDED_START) {
155 // i. Set generator.[[AsyncGeneratorState]] to completed.
156 // ii. Set state to completed.
157 state = JSAsyncGeneratorState::COMPLETED;
158 generator->SetAsyncGeneratorState(state);
159 }
160 // b. If state is completed, then
161 if (state == JSAsyncGeneratorState::COMPLETED) {
162 // i. If completion.[[Type]] is return, then
163 if (completion->GetType() == CompletionRecordType::RETURN) {
164 // 1. Set generator.[[AsyncGeneratorState]] to awaiting-return.
165 generator->SetAsyncGeneratorState(JSAsyncGeneratorState::AWAITING_RETURN);
166 // 2. Let promise be ? PromiseResolve(%Promise%, completion.[[Value]]).
167 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
168 JSHandle<JSTaggedValue> val(thread, completion->GetValue());
169 JSTaggedValue promise = PromiseResolve(thread,
170 JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()), val);
171 JSHandle<JSPromise> handPromise(thread, promise);
172 // 3. Let stepsFulfilled be the algorithm steps defined in
173 // AsyncGeneratorResumeNext Return Processor Fulfilled Functions.
174 // 4. Let lengthFulfilled be the number of non-optional parameters of the
175 // function definition in AsyncGeneratorResumeNext Return Processor Fulfilled Functions.
176 // 5. Let onFulfilled be ! CreateBuiltinFunction(stepsFulfilled,
177 // lengthFulfilled, "", « [[Generator]] »).
178 // 7. Let stepsRejected be the algorithm steps defined in AsyncGeneratorResumeNext
179 // Return Processor Rejected Functions.
180 // 8. Let lengthRejected be the number of non-optional parameters of the function definition in
181 // AsyncGeneratorResumeNext Return Processor Rejected Functions.
182 // 9. Let onRejected be ! CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[Generator]] »).
183 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
184 JSHandle<JSAsyncGeneratorResNextRetProRstFtn> onFulfilled =
185 factory->NewJSAsyGenResNextRetProRstFulfilledFtn();
186 onFulfilled->SetAsyncGeneratorObject(thread, generator);
187
188 JSHandle<JSAsyncGeneratorResNextRetProRstFtn> onFulRejected =
189 factory->NewJSAsyGenResNextRetProRstRejectedFtn();
190 onFulRejected->SetAsyncGeneratorObject(thread, generator);
191
192 // 11. Perform ! PerformPromiseThen(promise, onFulfilled, onRejected).
193 JSHandle<PromiseCapability> tcap =
194 JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
195 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
196 [[maybe_unused]] JSTaggedValue pres = BuiltinsPromise::PerformPromiseThen(
197 thread, handPromise, JSHandle<JSTaggedValue>::Cast(onFulfilled),
198 JSHandle<JSTaggedValue>::Cast(onFulRejected), tcap);
199 // 12. Return undefined.
200 return JSTaggedValue::Undefined();
201 } else {
202 // 1. Assert: completion.[[Type]] is throw.
203 ASSERT(completion->GetType() == CompletionRecordType::THROW);
204 // 2. Perform ! AsyncGeneratorReject(generator, completion.[[Value]]).
205 JSHandle<JSTaggedValue> comVal(thread, completion->GetValue());
206 AsyncGeneratorReject(thread, generator, comVal);
207 // 3. Return undefined.
208 return JSTaggedValue::Undefined();
209 }
210 }
211 // 11. Else if state is completed, return ! AsyncGeneratorResolve(generator, undefined, true).
212 } else if (state == JSAsyncGeneratorState::COMPLETED) {
213 JSHandle<JSTaggedValue> comVal(thread, JSTaggedValue::Undefined());
214 return AsyncGeneratorResolve(thread, generator, comVal, true);
215 }
216 // 12. Assert: state is either suspendedStart or suspendedYield.
217 ASSERT((state == JSAsyncGeneratorState::SUSPENDED_START) ||
218 (state == JSAsyncGeneratorState::SUSPENDED_YIELD));
219 // 13. Let genContext be generator.[[AsyncGeneratorContext]].
220 JSTaggedValue val = generator->GetGeneratorContext();
221
222 JSHandle<GeneratorContext> genContext(thread, val);
223 // 14. Let callerContext be the running execution context.
224 // 15. Suspend callerContext.
225 // 16. Set generator.[[AsyncGeneratorState]] to executing.
226 // 17. Push genContext onto the execution context stack; genContext is now the running execution context.
227 // 18. Resume the suspended evaluation of genContext using completion as the result of the operation that
228 // suspended it. Let result be the completion record returned by the resumed computation.
229 // 19. Assert: result is never an abrupt completion.
230 // 20. Assert: When we return here, genContext has already been removed from the execution context stack and
231 // callerContext is the currently running execution context.
232 // 21. Return undefined.
233 generator->SetAsyncGeneratorState(JSAsyncGeneratorState::EXECUTING);
234
235 if (completion->GetType() == CompletionRecordType::NORMAL) {
236 AsyncGeneratorHelper::Next(thread, genContext, completion->GetValue());
237 }
238 if (completion->GetType() == CompletionRecordType::RETURN) {
239 AsyncGeneratorHelper::Return(thread, genContext, completion);
240 }
241 if (completion->GetType() == CompletionRecordType::THROW) {
242 AsyncGeneratorHelper::Throw(thread, genContext, completion);
243 }
244 return JSTaggedValue::Undefined();
245 }
246
AsyncGeneratorEnqueue(JSThread * thread,const JSHandle<JSTaggedValue> & gen,const JSHandle<CompletionRecord> completionRecord)247 JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorEnqueue(JSThread *thread, const JSHandle<JSTaggedValue> &gen,
248 const JSHandle<CompletionRecord> completionRecord)
249 {
250 // 1. Let promiseCapability be ! NewPromiseCapability(%Promise%).
251 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
252 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
253 JSHandle<PromiseCapability> pcap =
254 JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
255 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
256 // 2. Let check be AsyncGeneratorValidate(generator, generatorBrand).
257 AsyncGeneratorValidate(thread, gen, JSTaggedValue::Undefined());
258 // 3. If check is an abrupt completion, then
259 if (thread->HasPendingException()) {
260 thread->ClearException();
261 // a. Let badGeneratorError be a newly created TypeError object.
262 JSHandle<JSObject> resolutionError =
263 factory->GetJSError(ErrorType::TYPE_ERROR, "Resolve: The promise and resolution cannot be the same.");
264 // b. Perform ! Call(promiseCapability.[[Reject]], undefined, « badGeneratorError »).
265 const GlobalEnvConstants *constants = thread->GlobalConstants();
266 JSHandle<JSTaggedValue> rstErr = JSHandle<JSTaggedValue>::Cast(resolutionError);
267 JSHandle<JSTaggedValue> reject(thread, pcap->GetReject());
268 JSHandle<JSTaggedValue> thisArg = constants->GetHandledUndefined();
269 JSHandle<JSTaggedValue> undefined = constants->GetHandledUndefined();
270 EcmaRuntimeCallInfo* info =
271 EcmaInterpreter::NewRuntimeCallInfo(thread, reject, thisArg, undefined, 1);
272 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
273 info->SetCallArg(rstErr.GetTaggedValue());
274 [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
275 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
276
277 // c. Return promiseCapability.[[Promise]].
278 JSHandle<JSObject> promise(thread, pcap->GetPromise());
279 return promise.GetTaggedValue();
280 }
281 // 4. Let queue be generator.[[AsyncGeneratorQueue]].
282 JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, gen);
283 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
284 JSHandle<JSAsyncGeneratorObject> generator = JSHandle<JSAsyncGeneratorObject>::Cast(obj);
285 JSHandle<TaggedQueue> queue(thread, generator->GetAsyncGeneratorQueue());
286 // 5. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }.
287 ObjectFactory *fty = thread->GetEcmaVM()->GetFactory();
288 JSHandle<AsyncGeneratorRequest> asyncGeneratorRst = fty->NewAsyncGeneratorRequest();
289
290 asyncGeneratorRst->SetCompletion(thread, completionRecord);
291 asyncGeneratorRst->SetCapability(thread, pcap);
292 // 6. Append request to the end of queue.
293
294 TaggedQueue *newQueue = TaggedQueue::Push(thread, queue, JSHandle<JSTaggedValue>::Cast(asyncGeneratorRst));
295 generator->SetAsyncGeneratorQueue(thread, JSTaggedValue(newQueue));
296
297 // 7. Let state be generator.[[AsyncGeneratorState]].
298 JSAsyncGeneratorState state = generator->GetAsyncGeneratorState();
299 // 8. If state is not executing, then
300 if (state != JSAsyncGeneratorState::EXECUTING) {
301 // a. Perform ! AsyncGeneratorResumeNext(generator).
302 AsyncGeneratorResumeNext(thread, generator);
303 }
304 // 9. Return promiseCapability.[[Promise]].
305 JSHandle<JSObject> promise(thread, pcap->GetPromise());
306 return promise.GetTaggedValue();
307 }
308
PromiseResolve(JSThread * thread,const JSHandle<JSTaggedValue> promise,const JSHandle<JSTaggedValue> value)309 JSTaggedValue JSAsyncGeneratorObject::PromiseResolve(JSThread *thread, const JSHandle<JSTaggedValue> promise,
310 const JSHandle<JSTaggedValue> value)
311 {
312 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
313 ASSERT(promise->IsECMAObject());
314 if (value->IsJSPromise()) {
315 JSHandle<JSTaggedValue> ctorKey(globalConst->GetHandledConstructorString());
316 JSHandle<JSTaggedValue> ctorValue = JSObject::GetProperty(thread, value, ctorKey).GetValue();
317 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
318 if (JSTaggedValue::SameValue(ctorValue.GetTaggedValue(), promise.GetTaggedValue())) {
319 return value.GetTaggedValue();
320 }
321 }
322 JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, promise);
323 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
324 JSHandle<JSTaggedValue> resolve(thread, promiseCapability->GetResolve());
325 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
326 JSHandle<JSTaggedValue> thisArg = globalConst->GetHandledUndefined();
327 EcmaRuntimeCallInfo* info =
328 EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, thisArg, undefined, 1);
329 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
330 info->SetCallArg(value.GetTaggedValue());
331 [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
332
333 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
334 JSHandle<JSPromise> promiseObj(thread, promiseCapability->GetPromise());
335 return promiseObj.GetTaggedValue();
336 }
337
ProcessorFulfilledFunc(EcmaRuntimeCallInfo * argv)338 JSTaggedValue JSAsyncGeneratorObject::ProcessorFulfilledFunc(EcmaRuntimeCallInfo *argv)
339 {
340 // 1. Let F be the active function object.
341 JSThread *thread = argv->GetThread();
342 JSHandle<JSAsyncGeneratorResNextRetProRstFtn> asyncResNextRtnPro =
343 JSHandle<JSAsyncGeneratorResNextRetProRstFtn>::Cast(base::BuiltinsBase::GetConstructor(argv));
344 JSHandle<JSAsyncGeneratorObject> asyncGen(thread, asyncResNextRtnPro->GetAsyncGeneratorObject());
345
346 // 2. Set F.[[Generator]].[[AsyncGeneratorState]] to completed.
347 asyncGen->SetAsyncGeneratorState(JSAsyncGeneratorState::COMPLETED);
348
349 // 3. Return ! AsyncGeneratorResolve(F.[[Generator]], value, true).
350 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, 0);
351 return AsyncGeneratorResolve(thread, asyncGen, value, true);
352 }
353
ProcessorRejectedFunc(EcmaRuntimeCallInfo * argv)354 JSTaggedValue JSAsyncGeneratorObject::ProcessorRejectedFunc(EcmaRuntimeCallInfo *argv)
355 {
356 // 1. Let F be the active function object.
357 JSThread *thread = argv->GetThread();
358 JSHandle<JSAsyncGeneratorResNextRetProRstFtn> asyncResNextRtnPro =
359 JSHandle<JSAsyncGeneratorResNextRetProRstFtn>::Cast(base::BuiltinsBase::GetConstructor(argv));
360 JSHandle<JSAsyncGeneratorObject> asyncGen(thread, asyncResNextRtnPro->GetAsyncGeneratorObject());
361 // 2. Set F.[[Generator]].[[AsyncGeneratorState]] to completed.
362 asyncGen->SetAsyncGeneratorState(JSAsyncGeneratorState::COMPLETED);
363 // 3. Return ! AsyncGeneratorReject(F.[[Generator]], reason).
364 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, 0);
365 return AsyncGeneratorReject(thread, asyncGen, value);
366 }
367 } // namespace panda::ecmascript
368