• 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/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