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