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