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