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