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