• 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/builtins/builtins_promise_job.h"
17 
18 #include "ecmascript/base/path_helper.h"
19 #include "ecmascript/ecma_macros.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/interpreter/interpreter.h"
22 #include "ecmascript/js_function.h"
23 #include "ecmascript/js_handle.h"
24 #include "ecmascript/jspandafile/js_pandafile_executor.h"
25 #include "ecmascript/jspandafile/js_pandafile_manager.h"
26 #include "ecmascript/platform/file.h"
27 #include "ecmascript/js_promise.h"
28 #include "ecmascript/js_tagged_value.h"
29 #include "ecmascript/module/js_module_manager.h"
30 #include "ecmascript/require/js_cjs_module.h"
31 #include "libpandabase/macros.h"
32 
33 namespace panda::ecmascript::builtins {
34 using PathHelper = base::PathHelper;
PromiseReactionJob(EcmaRuntimeCallInfo * argv)35 JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv)
36 {
37     ASSERT(argv);
38     BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, Reaction);
39     JSThread *thread = argv->GetThread();
40     [[maybe_unused]] EcmaHandleScope handleScope(thread);
41     // 1. Assert: reaction is a PromiseReaction Record.
42     JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
43     ASSERT(value->IsPromiseReaction());
44     JSHandle<PromiseReaction> reaction = JSHandle<PromiseReaction>::Cast(value);
45     JSHandle<JSTaggedValue> argument = GetCallArg(argv, 1);
46 
47     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
48     // 2. Let promiseCapability be reaction.[[Capabilities]].
49     JSHandle<PromiseCapability> capability(thread, reaction->GetPromiseCapability());
50     // 3. Let handler be reaction.[[Handler]].
51     JSHandle<JSTaggedValue> handler(thread, reaction->GetHandler());
52     JSHandle<JSTaggedValue> call(thread, capability->GetResolve());
53     const int32_t argsLength = 1;
54     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
55     EcmaRuntimeCallInfo *runtimeInfo =
56         EcmaInterpreter::NewRuntimeCallInfo(thread, call, undefined, undefined, argsLength);
57     if (handler->IsString()) {
58         // 4. If handler is "Identity", let handlerResult be NormalCompletion(argument).
59         // 5. Else if handler is "Thrower", let handlerResult be Completion{[[type]]: throw, [[value]]: argument,
60         // [[target]]: empty}.
61         runtimeInfo->SetCallArg(argument.GetTaggedValue());
62         if (EcmaStringAccessor::StringsAreEqual(handler.GetObject<EcmaString>(),
63             globalConst->GetHandledThrowerString().GetObject<EcmaString>())) {
64             runtimeInfo->SetFunction(capability->GetReject());
65         }
66     } else {
67         // 6. Else, let handlerResult be Call(handler, undefined, «argument»).
68         EcmaRuntimeCallInfo *info =
69             EcmaInterpreter::NewRuntimeCallInfo(thread, handler, undefined, undefined, argsLength);
70         info->SetCallArg(argument.GetTaggedValue());
71         JSTaggedValue taggedValue = JSFunction::Call(info);
72         // 7. If handlerResult is an abrupt completion, then
73         // a. Let status be Call(promiseCapability.[[Reject]], undefined, «handlerResult.[[value]]»).
74         // b. NextJob Completion(status).
75         if (thread->HasPendingException()) {
76             JSHandle<JSTaggedValue> throwValue = JSPromise::IfThrowGetThrowValue(thread);
77             runtimeInfo->SetCallArg(throwValue.GetTaggedValue());
78             thread->ClearException();
79             runtimeInfo->SetFunction(capability->GetReject());
80         } else {
81             runtimeInfo->SetCallArg(taggedValue);
82         }
83     }
84     // 8. Let status be Call(promiseCapability.[[Resolve]], undefined, «handlerResult.[[value]]»).
85     return JSFunction::Call(runtimeInfo);
86 }
87 
PromiseResolveThenableJob(EcmaRuntimeCallInfo * argv)88 JSTaggedValue BuiltinsPromiseJob::PromiseResolveThenableJob(EcmaRuntimeCallInfo *argv)
89 {
90     ASSERT(argv);
91     BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, ResolveThenableJob);
92     JSThread *thread = argv->GetThread();
93     [[maybe_unused]] EcmaHandleScope handleScope(thread);
94     JSHandle<JSTaggedValue> promise = GetCallArg(argv, 0);
95     ASSERT(promise->IsJSPromise());
96     // 1. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
97     JSHandle<ResolvingFunctionsRecord> resolvingFunctions =
98         JSPromise::CreateResolvingFunctions(thread, JSHandle<JSPromise>::Cast(promise));
99     JSHandle<JSTaggedValue> thenable = GetCallArg(argv, 1);
100     JSHandle<JSTaggedValue> then = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
101 
102     // 2. Let thenCallResult be Call(then, thenable, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»).
103     const int32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»
104     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
105     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, then, thenable, undefined, argsLength);
106     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
107     info->SetCallArg(resolvingFunctions->GetResolveFunction(), resolvingFunctions->GetRejectFunction());
108     JSTaggedValue result = JSFunction::Call(info);
109     JSHandle<JSTaggedValue> thenResult(thread, result);
110     // 3. If thenCallResult is an abrupt completion,
111     // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «thenCallResult.[[value]]»).
112     // b. NextJob Completion(status).
113     if (thread->HasPendingException()) {
114         thenResult = JSPromise::IfThrowGetThrowValue(thread);
115         thread->ClearException();
116         JSHandle<JSTaggedValue> reject(thread, resolvingFunctions->GetRejectFunction());
117         EcmaRuntimeCallInfo *runtimeInfo =
118             EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
119         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
120         runtimeInfo->SetCallArg(thenResult.GetTaggedValue());
121         return JSFunction::Call(runtimeInfo);
122     }
123     // 4. NextJob Completion(thenCallResult).
124     return result;
125 }
126 
DynamicImportJob(EcmaRuntimeCallInfo * argv)127 JSTaggedValue BuiltinsPromiseJob::DynamicImportJob(EcmaRuntimeCallInfo *argv)
128 {
129     ASSERT(argv);
130     BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, DynamicImportJob);
131     JSThread *thread = argv->GetThread();
132     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
133     EcmaVM *vm = thread->GetEcmaVM();
134     [[maybe_unused]] EcmaHandleScope handleScope(thread);
135 
136     JSHandle<JSPromiseReactionsFunction> resolve(GetCallArg(argv, 0));
137     JSHandle<JSPromiseReactionsFunction> reject(GetCallArg(argv, 1));   // 1 : first argument
138     JSHandle<EcmaString> dirPath(GetCallArg(argv, 2));                  // 2 : second argument
139     JSHandle<JSTaggedValue> specifier(GetCallArg(argv, 3));             // 3 : third argument
140     JSHandle<JSTaggedValue> recordName(GetCallArg(argv, 4));            // 4 : fourth argument
141 
142     // Let specifierString be Completion(ToString(specifier))
143     JSHandle<EcmaString> specifierString = JSTaggedValue::ToString(thread, specifier);
144     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
145 
146     JSHandle<EcmaString> moduleName;
147     CString entryPoint = JSPandaFile::ENTRY_MAIN_FUNCTION;
148     CString baseFilename = ConvertToString(dirPath.GetTaggedValue());
149     CString fileNameStr = "";
150     if (recordName->IsUndefined()) {
151         moduleName = ResolveFilenameFromNative(thread, dirPath.GetTaggedValue(),
152                                                specifierString.GetTaggedValue());
153         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
154         fileNameStr = ConvertToString(moduleName.GetTaggedValue());
155     } else {
156         CString recordNameStr = ConvertToString(recordName.GetTaggedValue());
157         CString requestModule = ConvertToString(specifier.GetTaggedValue());
158         const JSPandaFile *jsPandaFile =
159             JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFilename, recordNameStr.c_str());
160         if (jsPandaFile == nullptr) {
161             CString msg = "Load file with filename '" + baseFilename + "' failed, recordName '" + recordNameStr + "'";
162             JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue();
163             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, CatchException(thread, reject));
164         }
165 
166         entryPoint =
167             PathHelper::ConcatFileNameWithMerge(thread, jsPandaFile, baseFilename, recordNameStr, requestModule);
168         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
169         fileNameStr = baseFilename;
170         moduleName = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(entryPoint);
171     }
172 
173     const JSPandaFile *jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, fileNameStr,
174                                                                                         entryPoint);
175     if (jsPandaFile == nullptr) {
176         CString msg = "Load file with filename '" + fileNameStr + "' failed, recordName '" + entryPoint + "'";
177         JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue();
178         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, CatchException(thread, reject));
179     }
180     bool isModule = jsPandaFile->IsModule(thread, entryPoint);
181     JSMutableHandle<JSTaggedValue> moduleNamespace(thread, JSTaggedValue::Undefined());
182     if (!vm->GetModuleManager()->IsImportedModuleLoaded(moduleName.GetTaggedValue())) {
183         if (!JSPandaFileExecutor::ExecuteFromFile(thread, fileNameStr.c_str(), entryPoint.c_str(), false, true)) {
184             CString msg = "Cannot execute request dynamic-imported module : " + entryPoint;
185             JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue();
186             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, CatchException(thread, reject));
187         }
188     }
189     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
190 
191     if (!isModule) {
192         moduleNamespace.Update(vm->GetGlobalEnv()->GetExportOfScript());
193     } else {
194         // b. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
195         JSHandle<SourceTextModule> moduleRecord =
196             vm->GetModuleManager()->HostGetImportedModule(moduleName.GetTaggedValue());
197         // d. Let namespace be ? GetModuleNamespace(moduleRecord).
198         moduleNamespace.Update(SourceTextModule::GetModuleNamespace(thread, moduleRecord));
199     }
200     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
201     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
202     EcmaRuntimeCallInfo *info =
203         EcmaInterpreter::NewRuntimeCallInfo(thread,
204                                             JSHandle<JSTaggedValue>(resolve),
205                                             undefined, undefined, 1);
206     info->SetCallArg(moduleNamespace.GetTaggedValue());
207     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
208     return JSFunction::Call(info);
209 }
210 
CatchException(JSThread * thread,JSHandle<JSPromiseReactionsFunction> reject)211 JSTaggedValue BuiltinsPromiseJob::CatchException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> reject)
212 {
213     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
214     ASSERT(thread->HasPendingException());
215     JSHandle<JSTaggedValue> thenResult = JSPromise::IfThrowGetThrowValue(thread);
216     thread->ClearException();
217     JSHandle<JSTaggedValue> rejectfun(reject);
218     EcmaRuntimeCallInfo *runtimeInfo =
219         EcmaInterpreter::NewRuntimeCallInfo(thread, rejectfun, undefined, undefined, 1);
220     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
221     runtimeInfo->SetCallArg(thenResult.GetTaggedValue());
222     return JSFunction::Call(runtimeInfo);
223 }
224 }  // namespace panda::ecmascript::builtins
225