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_async_function.h"
17
18 #include "ecmascript/async_generator_helper.h"
19 #include "ecmascript/builtins/builtins_promise.h"
20 #include "ecmascript/builtins/builtins_promise_handler.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/generator_helper.h"
23 #include "ecmascript/global_env.h"
24 #include "ecmascript/interpreter/interpreter.h"
25 #include "ecmascript/js_async_generator_object.h"
26 #include "ecmascript/js_promise.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/object_factory.h"
29
30 namespace panda::ecmascript {
31 using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler;
32 using BuiltinsPromise = builtins::BuiltinsPromise;
33
AsyncFunctionAwait(JSThread * thread,const JSHandle<JSAsyncFuncObject> & asyncFuncObj,const JSHandle<JSTaggedValue> & value)34 void JSAsyncFunction::AsyncFunctionAwait(JSThread *thread, const JSHandle<JSAsyncFuncObject> &asyncFuncObj,
35 const JSHandle<JSTaggedValue> &value)
36 {
37 // 1.Let asyncContext be the running execution context.
38 auto vm = thread->GetEcmaVM();
39 ObjectFactory *factory = vm->GetFactory();
40
41 JSHandle<JSTaggedValue> asyncCtxt(thread, asyncFuncObj->GetGeneratorContext());
42
43 // 2.Let promiseCapability be ! NewPromiseCapability(%Promise%).
44 JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
45 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
46 JSHandle<PromiseCapability> pcap =
47 JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
48 RETURN_IF_ABRUPT_COMPLETION(thread);
49
50 // 3.Let resolveResult be ! Call(promiseCapability.[[Resolve]], undefined, « value »).
51 JSHandle<JSTaggedValue> resolve(thread, pcap->GetResolve());
52 JSHandle<JSTaggedValue> thisArg = globalConst->GetHandledUndefined();
53
54 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
55 EcmaRuntimeCallInfo *info =
56 EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, thisArg, undefined, 1);
57 RETURN_IF_ABRUPT_COMPLETION(thread);
58 info->SetCallArg(value.GetTaggedValue());
59 [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
60 RETURN_IF_ABRUPT_COMPLETION(thread);
61
62 // 4.Let onFulfilled be a new built-in function object as defined in AsyncFunction Awaited Fulfilled.
63 JSHandle<JSAsyncAwaitStatusFunction> fulFunc = factory->NewJSAsyncAwaitStatusFunction(
64 MethodIndex::BUILTINS_PROMISE_HANDLER_ASYNC_AWAIT_FULFILLED);
65
66 // 5.Let onRejected be a new built-in function object as defined in AsyncFunction Awaited Rejected.
67 JSHandle<JSAsyncAwaitStatusFunction> rejFunc = factory->NewJSAsyncAwaitStatusFunction(
68 MethodIndex::BUILTINS_PROMISE_HANDLER_ASYNC_AWAIT_REJECTED);
69
70 // 6.Set onFulfilled.[[AsyncContext]] to asyncContext.
71 // 7.Set onRejected.[[AsyncContext]] to asyncContext.
72 fulFunc->SetAsyncContext(thread, asyncCtxt);
73 rejFunc->SetAsyncContext(thread, asyncCtxt);
74
75 // 8.Let throwawayCapability be ! NewPromiseCapability(%Promise%).
76 // 9.Set throwawayCapability.[[Promise]].[[PromiseIsHandled]] to true.
77 JSHandle<PromiseCapability> tcap =
78 JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
79 RETURN_IF_ABRUPT_COMPLETION(thread);
80 JSHandle<JSPromise>(thread, tcap->GetPromise())->SetPromiseIsHandled(true);
81
82 // 10.Perform ! PerformPromiseThen(promiseCapability.[[Promise]], onFulfilled, onRejected, throwawayCapability).
83 JSHandle<JSPromise> promise(thread, pcap->GetPromise());
84 [[maybe_unused]] JSTaggedValue pres = BuiltinsPromise::PerformPromiseThen(
85 thread, promise, JSHandle<JSTaggedValue>::Cast(fulFunc), JSHandle<JSTaggedValue>::Cast(rejFunc), tcap);
86
87 // 11.Remove asyncContext from the execution context stack and restore the execution context that
88 // is at the top of the execution context stack as the running execution context.
89 // 12.Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion
90 // resumptionValue the following steps will be performed:
91 // a.Return resumptionValue.
92 // 13.Return.
93 }
94
AsyncFunctionAwait(JSThread * thread,const JSHandle<JSTaggedValue> & asyncFuncObj,const JSHandle<JSTaggedValue> & value)95 void JSAsyncFunction::AsyncFunctionAwait(JSThread *thread, const JSHandle<JSTaggedValue> &asyncFuncObj,
96 const JSHandle<JSTaggedValue> &value)
97 {
98 // 1.Let asyncContext be the running execution context.
99 auto vm = thread->GetEcmaVM();
100 ObjectFactory *factory = vm->GetFactory();
101 JSHandle<JSTaggedValue> asyncCtxt;
102 if (asyncFuncObj->IsAsyncGeneratorObject()) {
103 JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, asyncFuncObj);
104 RETURN_IF_ABRUPT_COMPLETION(thread);
105 JSHandle<JSAsyncGeneratorObject> asyncGen = JSHandle<JSAsyncGeneratorObject>::Cast(obj);
106 asyncCtxt = JSHandle<JSTaggedValue>(thread, asyncGen->GetGeneratorContext());
107 } else {
108 JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, asyncFuncObj);
109 RETURN_IF_ABRUPT_COMPLETION(thread);
110 JSHandle<JSAsyncFuncObject> asyncFun = JSHandle<JSAsyncFuncObject>::Cast(obj);
111 asyncCtxt = JSHandle<JSTaggedValue>(thread, asyncFun->GetGeneratorContext());
112 }
113
114 // 2.Let promise be ? PromiseResolve(%Promise%, value).
115 JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
116 JSHandle<JSTaggedValue> promiseValue =
117 builtins::BuiltinsPromiseHandler::PromiseResolve(thread,
118 JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()),
119 value);
120 RETURN_IF_ABRUPT_COMPLETION(thread);
121 // 4.Let onFulfilled be a new built-in function object as defined in AsyncFunction Awaited Fulfilled.
122 JSHandle<JSAsyncAwaitStatusFunction> fulFunc = factory->NewJSAsyncAwaitStatusFunction(
123 MethodIndex::BUILTINS_PROMISE_HANDLER_ASYNC_AWAIT_FULFILLED);
124
125 // 5.Let onRejected be a new built-in function object as defined in AsyncFunction Awaited Rejected.
126 JSHandle<JSAsyncAwaitStatusFunction> rejFunc = factory->NewJSAsyncAwaitStatusFunction(
127 MethodIndex::BUILTINS_PROMISE_HANDLER_ASYNC_AWAIT_REJECTED);
128
129 // 6.Set onFulfilled.[[AsyncContext]] to asyncContext.
130 // 7.Set onRejected.[[AsyncContext]] to asyncContext.
131 fulFunc->SetAsyncContext(thread, asyncCtxt);
132 rejFunc->SetAsyncContext(thread, asyncCtxt);
133
134 // 8.Let throwawayCapability be ! NewPromiseCapability(%Promise%).
135 // 9.Set throwawayCapability.[[Promise]].[[PromiseIsHandled]] to true.
136 JSHandle<PromiseCapability> tcap =
137 JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
138 RETURN_IF_ABRUPT_COMPLETION(thread);
139 JSHandle<JSPromise>(thread, tcap->GetPromise())->SetPromiseIsHandled(true);
140
141 // 10.Perform ! PerformPromiseThen(promiseCapability.[[Promise]], onFulfilled, onRejected, throwawayCapability).
142 JSHandle<JSObject> promise = JSHandle<JSObject>::Cast(promiseValue);
143 BuiltinsPromise::PerformPromiseThen(thread, JSHandle<JSPromise>::Cast(promise),
144 JSHandle<JSTaggedValue>::Cast(fulFunc),
145 JSHandle<JSTaggedValue>::Cast(rejFunc), tcap);
146
147 // 11.Remove asyncContext from the execution context stack and restore the execution context that
148 // is at the top of the execution context stack as the running execution context.
149 // 12.Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion
150 // resumptionValue the following steps will be performed:
151 // a.Return resumptionValue.
152 // 13.Return.
153 }
154
AsyncFunctionAwaitFulfilled(JSThread * thread,const JSHandle<JSAsyncAwaitStatusFunction> & func,const JSHandle<JSTaggedValue> & value)155 JSHandle<JSTaggedValue> JSAsyncAwaitStatusFunction::AsyncFunctionAwaitFulfilled(
156 JSThread *thread, const JSHandle<JSAsyncAwaitStatusFunction> &func, const JSHandle<JSTaggedValue> &value)
157 {
158 // 1.Let asyncContext be F.[[AsyncContext]].
159 JSHandle<GeneratorContext> asyncCtxt(thread, func->GetAsyncContext());
160
161 JSHandle<JSTaggedValue> tagVal(thread, asyncCtxt->GetGeneratorObject());
162 if (tagVal->IsAsyncGeneratorObject()) {
163 AsyncGeneratorHelper::Next(thread, asyncCtxt, value.GetTaggedValue());
164 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined());
165 } else {
166 // 2.Let prevContext be the running execution context.
167 // 3.Suspend prevContext.
168 // 4.Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
169 // 5.Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the
170 // operation that suspended it. Let result be the value returned by the resumed computation.
171 GeneratorHelper::Next(thread, asyncCtxt, value.GetTaggedValue());
172 // 6.Assert: When we reach this step, asyncContext has already been removed from the execution context stack
173 // and prevContext is the currently running execution context.
174
175 // 7.Return Completion(result).
176 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined());
177 }
178 }
179
AsyncFunctionAwaitRejected(JSThread * thread,const JSHandle<JSAsyncAwaitStatusFunction> & func,const JSHandle<JSTaggedValue> & reason)180 JSHandle<JSTaggedValue> JSAsyncAwaitStatusFunction::AsyncFunctionAwaitRejected(
181 JSThread *thread, const JSHandle<JSAsyncAwaitStatusFunction> &func, const JSHandle<JSTaggedValue> &reason)
182 {
183 // 1.Let asyncContext be F.[[AsyncContext]].
184 JSHandle<GeneratorContext> asyncCtxt(thread, func->GetAsyncContext());
185
186 JSHandle<JSTaggedValue> tagVal(thread, asyncCtxt->GetGeneratorObject());
187 if (tagVal->IsAsyncGeneratorObject()) {
188 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
189 JSHandle<CompletionRecord> completionRecord =
190 factory->NewCompletionRecord(CompletionRecordType::THROW, reason);
191 AsyncGeneratorHelper::Throw(thread, asyncCtxt, completionRecord);
192 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined());
193 } else {
194 // 2.Let prevContext be the running execution context.
195 // 3.Suspend prevContext.
196 // 4.Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
197 // 5.Resume the suspended evaluation of asyncContext using Completion{[[Type]]: throw,
198 // [[Value]]: reason, [[Target]]: empty} as the result of the operation that suspended it.
199 // Let result be the value returned by the resumed computation.
200 JSHandle<JSObject> result = GeneratorHelper::Throw(thread, asyncCtxt, reason.GetTaggedValue());
201 // 6.Assert: When we reach this step, asyncContext has already been removed from the execution context stack
202 // and prevContext is the currently running execution context.
203
204 // 7.Return Completion(result).
205 return JSHandle<JSTaggedValue>::Cast(result);
206 }
207 }
208 } // namespace panda::ecmascript
209