• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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/interpreter/interpreter.h"
19 #include "ecmascript/jspandafile/js_pandafile_executor.h"
20 #include "ecmascript/jspandafile/js_pandafile_manager.h"
21 #include "ecmascript/js_promise.h"
22 #include "ecmascript/module/js_dynamic_import.h"
23 #include "ecmascript/module/js_module_deregister.h"
24 #include "ecmascript/module/js_module_manager.h"
25 #include "ecmascript/module/module_path_helper.h"
26 #include "ecmascript/module/module_tools.h"
27 #include "ecmascript/module/static/static_module_loader.h"
28 
29 namespace panda::ecmascript::builtins {
30 using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo;
31 using ModulePathHelper = ecmascript::ModulePathHelper;
32 using PathHelper = ecmascript::base::PathHelper;
33 using StaticModuleLoader = ecmascript::StaticModuleLoader;
34 
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(thread));
50     // 3. Let handler be reaction.[[Handler]].
51     JSHandle<JSTaggedValue> handler(thread, reaction->GetHandler(thread));
52     JSHandle<JSTaggedValue> call(thread, capability->GetResolve(thread));
53     const uint32_t argsLength = 1;
54     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
55     EcmaRuntimeCallInfo *runtimeInfo =
56         EcmaInterpreter::NewRuntimeCallInfo(thread, call, undefined, undefined, argsLength);
57     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
58     if (handler->IsString()) {
59         // 4. If handler is "Identity", let handlerResult be NormalCompletion(argument).
60         // 5. Else if handler is "Thrower", let handlerResult be Completion{[[type]]: throw, [[value]]: argument,
61         // [[target]]: empty}.
62         runtimeInfo->SetCallArg(argument.GetTaggedValue());
63         if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(),
64             JSHandle<EcmaString>(handler), JSHandle<EcmaString>(globalConst->GetHandledThrowerString()))) {
65             runtimeInfo->SetFunction(capability->GetReject(thread));
66         }
67     } else {
68         // 6. Else, let handlerResult be Call(handler, undefined, «argument»).
69         EcmaRuntimeCallInfo *info =
70             EcmaInterpreter::NewRuntimeCallInfo(thread, handler, undefined, undefined, argsLength);
71         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
72         info->SetCallArg(argument.GetTaggedValue());
73         JSTaggedValue taggedValue = JSFunction::Call(info);
74         // 7. If handlerResult is an abrupt completion, then
75         // a. Let status be Call(promiseCapability.[[Reject]], undefined, «handlerResult.[[value]]»).
76         // b. NextJob Completion(status).
77         if (thread->HasPendingException()) {
78             JSHandle<JSTaggedValue> throwValue = JSPromise::IfThrowGetThrowValue(thread);
79             runtimeInfo->SetCallArg(throwValue.GetTaggedValue());
80             thread->ClearException();
81             runtimeInfo->SetFunction(capability->GetReject(thread));
82         } else {
83             runtimeInfo->SetCallArg(taggedValue);
84         }
85     }
86     // 8. Let status be Call(promiseCapability.[[Resolve]], undefined, «handlerResult.[[value]]»).
87     return JSFunction::Call(runtimeInfo);
88 }
89 
PromiseResolveThenableJob(EcmaRuntimeCallInfo * argv)90 JSTaggedValue BuiltinsPromiseJob::PromiseResolveThenableJob(EcmaRuntimeCallInfo *argv)
91 {
92     ASSERT(argv);
93     BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, ResolveThenableJob);
94     JSThread *thread = argv->GetThread();
95     [[maybe_unused]] EcmaHandleScope handleScope(thread);
96     JSHandle<JSTaggedValue> promise = GetCallArg(argv, 0);
97     ASSERT(promise->IsJSPromise());
98     // 1. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
99     JSHandle<ResolvingFunctionsRecord> resolvingFunctions =
100         JSPromise::CreateResolvingFunctions(thread, JSHandle<JSPromise>::Cast(promise));
101     JSHandle<JSTaggedValue> thenable = GetCallArg(argv, 1);
102     JSHandle<JSTaggedValue> then = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
103 
104     // 2. Let thenCallResult be Call(then, thenable, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»).
105     const uint32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»
106     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
107     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, then, thenable, undefined, argsLength);
108     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
109     info->SetCallArg(resolvingFunctions->GetResolveFunction(thread), resolvingFunctions->GetRejectFunction(thread));
110     JSTaggedValue result = JSFunction::Call(info);
111     JSHandle<JSTaggedValue> thenResult(thread, result);
112     // 3. If thenCallResult is an abrupt completion,
113     // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «thenCallResult.[[value]]»).
114     // b. NextJob Completion(status).
115     if (thread->HasPendingException()) {
116         thenResult = JSPromise::IfThrowGetThrowValue(thread);
117         thread->ClearException();
118         JSHandle<JSTaggedValue> reject(thread, resolvingFunctions->GetRejectFunction(thread));
119         EcmaRuntimeCallInfo *runtimeInfo =
120             EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
121         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
122         runtimeInfo->SetCallArg(thenResult.GetTaggedValue());
123         return JSFunction::Call(runtimeInfo);
124     }
125     // 4. NextJob Completion(thenCallResult).
126     return result;
127 }
128 
DynamicImportJob(EcmaRuntimeCallInfo * argv)129 JSTaggedValue BuiltinsPromiseJob::DynamicImportJob(EcmaRuntimeCallInfo *argv)
130 {
131     ASSERT(argv);
132     BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, DynamicImportJob);
133     JSThread *thread = argv->GetThread();
134     EcmaVM *vm = thread->GetEcmaVM();
135     [[maybe_unused]] EcmaHandleScope handleScope(thread);
136 
137     JSHandle<JSPromiseReactionsFunction> resolve(GetCallArg(argv, 0));
138     JSHandle<JSPromiseReactionsFunction> reject(GetCallArg(argv, 1));   // 1 : reject method
139     JSHandle<EcmaString> dirPath(GetCallArg(argv, 2));                  // 2 : current file path(containing file name)
140     JSHandle<JSTaggedValue> specifier(GetCallArg(argv, 3));             // 3 : request module's path
141     JSHandle<JSTaggedValue> recordName(GetCallArg(argv, 4));            // 4 : js recordName or undefined
142 
143     // Let specifierString be Completion(ToString(specifier))
144     JSHandle<EcmaString> specifierString = JSTaggedValue::ToString(thread, specifier);
145     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
146 
147     // Resolve request module's ohmurl
148     CString entryPoint = JSPandaFile::ENTRY_MAIN_FUNCTION;
149     CString fileName = ModulePathHelper::Utf8ConvertToString(thread, dirPath.GetTaggedValue());
150     CString requestPath = ModulePathHelper::Utf8ConvertToString(thread, specifierString.GetTaggedValue());
151     LOG_ECMA(DEBUG) << "Start importing dynamic module : " << requestPath;
152     ModuleTraceScope moduleTraceScope(thread, "BuiltinsPromiseJob::DynamicImport:" + requestPath);
153     std::shared_ptr<JSPandaFile> curJsPandaFile;
154     CString recordNameStr;
155     if (!recordName->IsUndefined()) {
156         recordNameStr = ModulePathHelper::Utf8ConvertToString(thread, recordName.GetTaggedValue());
157         curJsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
158             thread, fileName, recordNameStr.c_str(), false, ExecuteTypes::STATIC);
159         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
160             HandleModuleException(thread, resolve, reject, specifierString));
161         if (curJsPandaFile == nullptr) {
162             LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << recordNameStr;
163         }
164         // translate requestPath to OhmUrl
165         if (vm->IsNormalizedOhmUrlPack()) {
166             ModulePathHelper::TranslateExpressionToNormalized(thread, curJsPandaFile.get(), fileName, recordNameStr,
167                 requestPath);
168             LOG_ECMA(DEBUG) << "Exit Translate Normalized OhmUrl for DynamicImport, resultPath: " << requestPath;
169             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
170                 HandleModuleException(thread, resolve, reject, specifierString));
171         } else if (ModulePathHelper::NeedTranstale(requestPath)) {
172             ModulePathHelper::TranstaleExpressionInput(curJsPandaFile.get(), requestPath);
173             LOG_ECMA(DEBUG) << "Exit Translate OhmUrl for DynamicImport, resultPath: " << requestPath;
174         }
175     }
176     // resolve native module
177     ModuleManager *moduleManager = thread->GetModuleManager();
178     if (SourceTextModule::IsNativeModule(requestPath)) {
179         return DynamicImport::ExecuteNativeOrJsonModule(thread, requestPath,
180             SourceTextModule::GetNativeModuleType(requestPath), resolve, reject);
181     }
182 
183     CString moduleName;
184     if (recordName->IsUndefined()) {
185         fileName = ResolveFilenameFromNative(thread, fileName, requestPath);
186         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
187             HandleModuleException(thread, resolve, reject, specifierString));
188         moduleName = fileName;
189     } else {
190         entryPoint =
191             ModulePathHelper::ConcatFileNameWithMerge(thread, curJsPandaFile.get(),
192                 fileName, recordNameStr, requestPath);
193         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
194             HandleModuleException(thread, resolve, reject, specifierString));
195         moduleName = entryPoint;
196     }
197     std::shared_ptr<JSPandaFile> jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
198         thread, fileName, entryPoint, false, ExecuteTypes::STATIC);
199     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
200         HandleModuleException(thread, resolve, reject, specifierString));
201     if (jsPandaFile == nullptr) {
202         LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << fileName;
203     }
204 
205     JSRecordInfo *recordInfo = jsPandaFile->CheckAndGetRecordInfo(entryPoint);
206     if (recordInfo == nullptr) {
207         CString normalizeStr = ModulePathHelper::ReformatPath(entryPoint);
208         CString msg = "Cannot find dynamic-import module '" + normalizeStr;
209         THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(),
210             HandleModuleException(thread, resolve, reject, specifierString));
211     }
212 
213     if (jsPandaFile->IsJson(recordInfo)) {
214         return DynamicImport::ExecuteNativeOrJsonModule(
215             thread, entryPoint, ModuleTypes::JSON_MODULE, resolve, reject, jsPandaFile.get());
216     }
217     // Loading request module.
218     thread->GetEcmaVM()->PushToDeregisterModuleList(entryPoint);
219     // IsInstantiatedModule is for lazy module to execute
220     if (!moduleManager->IsModuleLoaded(moduleName) || moduleManager->IsInstantiatedModule(moduleName)) {
221         if (!JSPandaFileExecutor::ExecuteFromAbcFile(
222             thread, fileName.c_str(), entryPoint.c_str(), false, ExecuteTypes::DYNAMIC)) {
223             CString msg = "Cannot execute request dynamic-imported module : " + entryPoint;
224             THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(),
225                 HandleModuleException(thread, resolve, reject, specifierString));
226         }
227     } else {
228         ModuleDeregister::ReviseLoadedModuleCount(thread, moduleName);
229     }
230 
231     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
232         HandleModuleException(thread, resolve, reject, specifierString));
233     JSMutableHandle<JSTaggedValue> moduleNamespace(thread, JSTaggedValue::Undefined());
234     // only support importing es module, or return a default object.
235     if (!jsPandaFile->IsModule(recordInfo)) {
236         moduleNamespace.Update(vm->GetGlobalEnv()->GetExportOfScript());
237     } else {
238         // b. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
239         JSHandle<SourceTextModule> moduleRecord =
240             moduleManager->GetImportedModule(moduleName);
241         moduleRecord->CheckAndThrowModuleError(thread);
242         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
243             HandleModuleException(thread, resolve, reject, specifierString));
244         JSHandle<JSTaggedValue> nameSp = SourceTextModule::GetModuleNamespace(thread, moduleRecord);
245         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
246             HandleModuleException(thread, resolve, reject, specifierString));
247         // d. Let namespace be ? GetModuleNamespace(moduleRecord).
248         moduleNamespace.Update(nameSp);
249     }
250     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
251     EcmaRuntimeCallInfo *info =
252         EcmaInterpreter::NewRuntimeCallInfo(thread,
253                                             JSHandle<JSTaggedValue>(resolve),
254                                             undefined, undefined, 1);
255     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
256         HandleModuleException(thread, resolve, reject, specifierString));
257     info->SetCallArg(moduleNamespace.GetTaggedValue());
258     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
259     return JSFunction::Call(info);
260 }
261 
CatchException(JSThread * thread,JSHandle<JSPromiseReactionsFunction> reject)262 JSTaggedValue BuiltinsPromiseJob::CatchException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> reject)
263 {
264     BUILTINS_API_TRACE(thread, PromiseJob, CatchException);
265     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
266     ASSERT(thread->HasPendingException());
267     JSHandle<JSTaggedValue> thenResult = JSPromise::IfThrowGetThrowValue(thread);
268     thread->ClearException();
269     JSHandle<JSTaggedValue> rejectfun(reject);
270     EcmaRuntimeCallInfo *runtimeInfo =
271         EcmaInterpreter::NewRuntimeCallInfo(thread, rejectfun, undefined, undefined, 1);
272     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
273     runtimeInfo->SetCallArg(thenResult.GetTaggedValue());
274     return JSFunction::Call(runtimeInfo);
275 }
276 
HandleModuleException(JSThread * thread,JSHandle<JSPromiseReactionsFunction> resolve,JSHandle<JSPromiseReactionsFunction> reject,JSHandle<EcmaString> specifierString)277 JSTaggedValue BuiltinsPromiseJob::HandleModuleException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> resolve,
278     JSHandle<JSPromiseReactionsFunction> reject, JSHandle<EcmaString> specifierString)
279 {
280     CString requestPath = ModulePathHelper::Utf8ConvertToString(thread, specifierString.GetTaggedValue());
281     return HandleModuleException(thread, resolve, reject, requestPath);
282 }
283 
HandleModuleException(JSThread * thread,JSHandle<JSPromiseReactionsFunction> resolve,JSHandle<JSPromiseReactionsFunction> reject,const CString & requestPath)284 JSTaggedValue BuiltinsPromiseJob::HandleModuleException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> resolve,
285     JSHandle<JSPromiseReactionsFunction> reject, const CString &requestPath)
286 {
287     ASSERT(thread->HasPendingException());
288     LOG_ECMA(DEBUG) << "start handle module exception " << requestPath;
289     // If the ohmurl is detected to be in compliance with the 1.0 prefix rule, then throw an exception directly
290     if (!StaticModuleLoader::CanTryLoadStaticModulePath(requestPath)) {
291         LOG_ECMA(DEBUG) << "handle dynamic module exception " << requestPath;
292         return CatchException(thread, reject);
293     }
294     LOG_ECMA(DEBUG) << "try to start load static module: " << requestPath;
295     JSHandle<JSTaggedValue> errorReuslt = JSPromise::IfThrowGetThrowValue(thread);
296     thread->ClearException();
297     EcmaVM *vm = thread->GetEcmaVM();
298     Local<JSValueRef> getEsModule = StaticModuleLoader::GetStaticModuleLoadFunc(vm);
299     if (!getEsModule->IsFunction(vm)) {
300         LOG_ECMA(DEBUG) << "napi static module function not found " << requestPath;
301         thread->SetException(errorReuslt.GetTaggedValue());
302         return CatchException(thread, reject);
303     }
304     // try load 1.2 module;
305     Local<FunctionRef> getEsModuleFunc = getEsModule;
306     ModuleManager *moduleManager = thread->GetModuleManager();
307     JSHandle<JSTaggedValue> exportObject = StaticModuleLoader::LoadStaticModule(thread, getEsModuleFunc, requestPath);
308     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
309     LOG_ECMA(DEBUG) << "load static module successfull, requestPath: " << requestPath;
310     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
311     EcmaRuntimeCallInfo *info =
312         EcmaInterpreter::NewRuntimeCallInfo(thread,
313                                             JSHandle<JSTaggedValue>(resolve),
314                                             undefined, undefined, 1);
315     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
316     info->SetCallArg(exportObject.GetTaggedValue());
317     return JSFunction::Call(info);
318 }
319 
320 }  // namespace panda::ecmascript::builtins
321