• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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_promise.h"
17 
18 #include "ecmascript/builtins/builtins_promise_handler.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/jobs/micro_job_queue.h"
22 
23 namespace panda::ecmascript {
24 using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler;
25 
CreateResolvingFunctions(JSThread * thread,const JSHandle<JSPromise> & promise)26 JSHandle<ResolvingFunctionsRecord> JSPromise::CreateResolvingFunctions(JSThread *thread,
27                                                                        const JSHandle<JSPromise> &promise)
28 {
29     if (thread->GetEcmaVM()->GetJSOptions().EnablePendingCheak()) {
30         thread->GetEcmaVM()->InsertAsyncStackTrace(promise);
31     }
32     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33     // 1. Let alreadyResolved be a new Record { [[value]]: false }.
34     JSHandle<PromiseRecord> record = factory->NewPromiseRecord();
35     record->SetValue(thread, JSTaggedValue::False());
36 
37     // 2. Let resolve be a new built-in function object as defined in Promise Resolve Functions (25.4.1.3.2).
38     JSHandle<JSPromiseReactionsFunction> resolve = factory->CreateJSPromiseReactionsFunction(
39         MethodIndex::BUILTINS_PROMISE_HANDLER_RESOLVE);
40     // 3. Set the [[Promise]] internal slot of resolve to promise.
41     resolve->SetPromise(thread, promise);
42     // 4. Set the [[AlreadyResolved]] internal slot of resolve to alreadyResolved.
43     resolve->SetAlreadyResolved(thread, record);
44     // 5. Let reject be a new built-in function object as defined in Promise Reject Functions (25.4.1.3.1).
45     JSHandle<JSPromiseReactionsFunction> reject = factory->CreateJSPromiseReactionsFunction(
46         MethodIndex::BUILTINS_PROMISE_HANDLER_REJECT);
47     // 6. Set the [[Promise]] internal slot of reject to promise.
48     reject->SetPromise(thread, promise);
49     // 7. Set the [[AlreadyResolved]] internal slot of reject to alreadyResolved.
50     reject->SetAlreadyResolved(thread, record);
51     // 8. Return a new Record { [[Resolve]]: resolve, [[Reject]]: reject }.
52     JSHandle<ResolvingFunctionsRecord> reactions = factory->NewResolvingFunctionsRecord();
53     reactions->SetResolveFunction(thread, resolve.GetTaggedValue());
54     reactions->SetRejectFunction(thread, reject.GetTaggedValue());
55     return reactions;
56 }
57 
FulfillPromise(JSThread * thread,const JSHandle<JSPromise> & promise,const JSHandle<JSTaggedValue> & value)58 JSTaggedValue JSPromise::FulfillPromise(JSThread *thread, const JSHandle<JSPromise> &promise,
59                                         const JSHandle<JSTaggedValue> &value)
60 {
61     if (thread->GetEcmaVM()->GetJSOptions().EnablePendingCheak()) {
62         thread->GetEcmaVM()->RemoveAsyncStackTrace(promise);
63     }
64     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
65     // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending".
66     ASSERT_PRINT(promise->GetPromiseState() == PromiseState::PENDING, "FulfillPromise: state must be pending");
67     // 2. Let reactions be the value of promise's [[PromiseFulfillReactions]] internal slot.
68     JSHandle<TaggedQueue> reactions(thread, promise->GetPromiseFulfillReactions(thread));
69     // 3. Set the value of promise's [[PromiseResult]] internal slot to value.
70     promise->SetPromiseResult(thread, value);
71     // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined.
72     promise->SetPromiseFulfillReactions<SKIP_BARRIER>(thread, JSTaggedValue::Undefined());
73     // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined.
74     promise->SetPromiseRejectReactions<SKIP_BARRIER>(thread, JSTaggedValue::Undefined());
75     // 6. Set the value of promise's [[PromiseState]] internal slot to "fulfilled".
76     promise->SetPromiseState(PromiseState::FULFILLED);
77     // 7. Return TriggerPromiseReactions(reactions, reason).
78     return TriggerPromiseReactions(thread, reactions, value);
79 }
80 
NewPromiseCapability(JSThread * thread,const JSHandle<JSTaggedValue> & obj)81 JSHandle<PromiseCapability> JSPromise::NewPromiseCapability(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
82 {
83     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
84     // 1. If IsConstructor(C) is false, throw a TypeError exception.
85     if (!obj->IsConstructor()) {
86         THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: obj is not constructor!",
87                                     factory->NewPromiseCapability());
88     }
89     // 2. NOTE C is assumed to be a constructor function that supports the parameter conventions of the Promise
90     //    constructor (see 25.4.3.1).
91     // 3. Let promiseCapability be a new PromiseCapability { [[Promise]]: undefined, [[Resolve]]: undefined,
92     //    [[Reject]]: undefined }.
93     JSHandle<PromiseCapability> promiseCapability = factory->NewPromiseCapability();
94     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
95     if (obj == env->GetPromiseFunction()) {
96         JSHandle<JSPromise> promise = factory->NewJSPromise();
97         JSHandle<ResolvingFunctionsRecord> resolvingFunctions = JSPromise::CreateResolvingFunctions(thread, promise);
98         promiseCapability->SetPromise(thread, promise);
99         auto resolveFunc = resolvingFunctions->GetResolveFunction(thread);
100         auto rejectFunc = resolvingFunctions->GetRejectFunction(thread);
101         promiseCapability->SetResolve(thread, resolveFunc);
102         promiseCapability->SetReject(thread, rejectFunc);
103         return promiseCapability;
104     }
105     // 4. Let executor be a new built-in function object as defined in GetCapabilitiesExecutor Functions
106     //    (25.4.1.5.1).
107     JSHandle<JSPromiseExecutorFunction> executor = factory->CreateJSPromiseExecutorFunction();
108     // 5. Set the [[Capability]] internal slot of executor to promiseCapability.
109     executor->SetCapability(thread, promiseCapability.GetTaggedValue());
110     // 6. Let promise be Construct(C, «executor»).
111     // 7. ReturnIfAbrupt(promise).
112     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
113     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, obj, undefined, undefined, 1);
114     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, factory->NewPromiseCapability());
115     info->SetCallArg(executor.GetTaggedValue());
116     JSTaggedValue result = JSFunction::Construct(info);
117     JSHandle<JSPromise> promise(thread, result);
118     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, factory->NewPromiseCapability());
119     // 8. If IsCallable(promiseCapability.[[Resolve]]) is false, throw a TypeError exception.
120     if (!promiseCapability->GetResolve(thread).IsCallable()) {
121         THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: resolve is not a callable function!",
122                                     factory->NewPromiseCapability());
123     }
124     // 9. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception.
125     if (!promiseCapability->GetReject(thread).IsCallable()) {
126         THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: reject is not a callable function!",
127                                     factory->NewPromiseCapability());
128     }
129     // 10. Set promiseCapability.[[Promise]] to promise.
130     promiseCapability->SetPromise(thread, promise);
131     // 11. Return promiseCapability.
132     return promiseCapability;
133 }
134 
IsPromise(const JSHandle<JSTaggedValue> & value)135 bool JSPromise::IsPromise(const JSHandle<JSTaggedValue> &value)
136 {
137     // 1. If Type(x) is not Object, return false.
138     if (!value->IsECMAObject()) {
139         return false;
140     }
141     // 2. If x does not have a [[PromiseState]] internal slot, return false.
142     if (!value->IsJSPromise()) {
143         return false;
144     }
145     // 3. Return true
146     return true;
147 }
148 
RejectPromise(JSThread * thread,const JSHandle<JSPromise> & promise,const JSHandle<JSTaggedValue> & reason)149 JSTaggedValue JSPromise::RejectPromise(JSThread *thread, const JSHandle<JSPromise> &promise,
150                                        const JSHandle<JSTaggedValue> &reason)
151 {
152     if (thread->GetEcmaVM()->GetJSOptions().EnablePendingCheak()) {
153         thread->GetEcmaVM()->RemoveAsyncStackTrace(promise);
154     }
155     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
156     // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending".
157     ASSERT_PRINT(promise->GetPromiseState() == PromiseState::PENDING, "RejectPromise: state must be pending");
158     // 2. Let reactions be the value of promise's [[PromiseRejectReactions]] internal slot.
159     JSHandle<TaggedQueue> reactions(thread,
160                                     TaggedQueue::Cast(promise->GetPromiseRejectReactions(thread).GetTaggedObject()));
161     // 3. Set the value of promise's [[PromiseResult]] internal slot to reason.
162     promise->SetPromiseResult(thread, reason);
163     // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined.
164     promise->SetPromiseFulfillReactions<SKIP_BARRIER>(thread, JSTaggedValue::Undefined());
165     // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined.
166     promise->SetPromiseRejectReactions<SKIP_BARRIER>(thread, JSTaggedValue::Undefined());
167     // 6. Set the value of promise's [[PromiseState]] internal slot to "rejected".
168     promise->SetPromiseState(PromiseState::REJECTED);
169     // 7. When a promise is rejected without any handlers, it is called with its operation argument set to "reject".
170     if (!promise->GetPromiseIsHandled()) {
171         thread->GetEcmaVM()->PromiseRejectionTracker(promise, reason, PromiseRejectionEvent::REJECT);
172     }
173     // 8. Return TriggerPromiseReactions(reactions, reason).
174     return TriggerPromiseReactions(thread, reactions, reason);
175 }
176 
TriggerPromiseReactions(JSThread * thread,const JSHandle<TaggedQueue> & reactions,const JSHandle<JSTaggedValue> & argument)177 JSTaggedValue JSPromise::TriggerPromiseReactions(JSThread *thread, const JSHandle<TaggedQueue> &reactions,
178                                                  const JSHandle<JSTaggedValue> &argument)
179 {
180     // 1. Repeat for each reaction in reactions, in original insertion order
181     // a. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «reaction, argument»).
182     JSHandle<job::MicroJobQueue> job = thread->GetEcmaVM()->GetMicroJobQueue();
183     JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
184     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
185     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
186     JSHandle<JSFunction> promiseReactionsJob(globalEnv->GetPromiseReactionJob());
187     JSMutableHandle<PromiseReaction> reaction(thread, JSTaggedValue::Undefined());
188     while (!reactions->Empty(thread)) {
189         reaction.Update(reactions->Pop(thread));
190         JSHandle<TaggedArray> arguments = factory->NewTaggedArray(2);  // 2 means the length of new array
191         arguments->Set(thread, 0, reaction);
192         arguments->Set(thread, 1, argument);
193         job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, arguments);
194     }
195     // 2. Return undefined.
196     return globalConst->GetUndefined();
197 }
198 
IfThrowGetThrowValue(JSThread * thread)199 JSHandle<JSTaggedValue> JSPromise::IfThrowGetThrowValue(JSThread *thread)
200 {
201     return JSHandle<JSTaggedValue>(thread, thread->GetException());
202 }
203 }  // namespace panda::ecmascript
204