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/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
28 namespace panda::ecmascript::builtins {
29 using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo;
30 using ModulePathHelper = ecmascript::ModulePathHelper;
31 using PathHelper = ecmascript::base::PathHelper;
32
PromiseReactionJob(EcmaRuntimeCallInfo * argv)33 JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv)
34 {
35 ASSERT(argv);
36 BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, Reaction);
37 JSThread *thread = argv->GetThread();
38 [[maybe_unused]] EcmaHandleScope handleScope(thread);
39 // 1. Assert: reaction is a PromiseReaction Record.
40 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
41 ASSERT(value->IsPromiseReaction());
42 JSHandle<PromiseReaction> reaction = JSHandle<PromiseReaction>::Cast(value);
43 JSHandle<JSTaggedValue> argument = GetCallArg(argv, 1);
44
45 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
46 // 2. Let promiseCapability be reaction.[[Capabilities]].
47 JSHandle<PromiseCapability> capability(thread, reaction->GetPromiseCapability());
48 // 3. Let handler be reaction.[[Handler]].
49 JSHandle<JSTaggedValue> handler(thread, reaction->GetHandler());
50 JSHandle<JSTaggedValue> call(thread, capability->GetResolve());
51 const uint32_t argsLength = 1;
52 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
53 EcmaRuntimeCallInfo *runtimeInfo =
54 EcmaInterpreter::NewRuntimeCallInfo(thread, call, undefined, undefined, argsLength);
55 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
56 if (handler->IsString()) {
57 // 4. If handler is "Identity", let handlerResult be NormalCompletion(argument).
58 // 5. Else if handler is "Thrower", let handlerResult be Completion{[[type]]: throw, [[value]]: argument,
59 // [[target]]: empty}.
60 runtimeInfo->SetCallArg(argument.GetTaggedValue());
61 if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(),
62 JSHandle<EcmaString>(handler), JSHandle<EcmaString>(globalConst->GetHandledThrowerString()))) {
63 runtimeInfo->SetFunction(capability->GetReject());
64 }
65 } else {
66 // 6. Else, let handlerResult be Call(handler, undefined, «argument»).
67 EcmaRuntimeCallInfo *info =
68 EcmaInterpreter::NewRuntimeCallInfo(thread, handler, undefined, undefined, argsLength);
69 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
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 uint32_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 EcmaVM *vm = thread->GetEcmaVM();
133 [[maybe_unused]] EcmaHandleScope handleScope(thread);
134
135 JSHandle<JSPromiseReactionsFunction> resolve(GetCallArg(argv, 0));
136 JSHandle<JSPromiseReactionsFunction> reject(GetCallArg(argv, 1)); // 1 : reject method
137 JSHandle<EcmaString> dirPath(GetCallArg(argv, 2)); // 2 : current file path(containing file name)
138 JSHandle<JSTaggedValue> specifier(GetCallArg(argv, 3)); // 3 : request module's path
139 JSHandle<JSTaggedValue> recordName(GetCallArg(argv, 4)); // 4 : js recordName or undefined
140
141 // Let specifierString be Completion(ToString(specifier))
142 JSHandle<EcmaString> specifierString = JSTaggedValue::ToString(thread, specifier);
143 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
144
145 // Resolve request module's ohmurl
146 CString entryPoint = JSPandaFile::ENTRY_MAIN_FUNCTION;
147 CString fileName = ModulePathHelper::Utf8ConvertToString(dirPath.GetTaggedValue());
148 CString requestPath = ModulePathHelper::Utf8ConvertToString(specifierString.GetTaggedValue());
149 LOG_ECMA(DEBUG) << "Start importing dynamic module : " << requestPath;
150 ModuleTraceScope moduleTraceScope(thread, "BuiltinsPromiseJob::DynamicImport:" + requestPath);
151 std::shared_ptr<JSPandaFile> curJsPandaFile;
152 CString recordNameStr;
153 if (!recordName->IsUndefined()) {
154 recordNameStr = ModulePathHelper::Utf8ConvertToString(recordName.GetTaggedValue());
155 curJsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
156 thread, fileName, recordNameStr.c_str(), false, ExecuteTypes::STATIC);
157 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
158 if (curJsPandaFile == nullptr) {
159 LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << recordNameStr;
160 }
161 // translate requestPath to OhmUrl
162 if (vm->IsNormalizedOhmUrlPack()) {
163 ModulePathHelper::TranslateExpressionToNormalized(thread, curJsPandaFile.get(), fileName, recordNameStr,
164 requestPath);
165 LOG_ECMA(DEBUG) << "Exit Translate Normalized OhmUrl for DynamicImport, resultPath: " << requestPath;
166 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
167 } else if (ModulePathHelper::NeedTranstale(requestPath)) {
168 ModulePathHelper::TranstaleExpressionInput(curJsPandaFile.get(), requestPath);
169 LOG_ECMA(DEBUG) << "Exit Translate OhmUrl for DynamicImport, resultPath: " << requestPath;
170 }
171 }
172 // resolve native module
173 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
174 if (SourceTextModule::IsNativeModule(requestPath)) {
175 return DynamicImport::ExecuteNativeOrJsonModule(thread, requestPath,
176 SourceTextModule::GetNativeModuleType(requestPath), resolve, reject);
177 }
178
179 CString moduleName;
180 if (recordName->IsUndefined()) {
181 fileName = ResolveFilenameFromNative(thread, fileName, requestPath);
182 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
183 moduleName = fileName;
184 } else {
185 entryPoint =
186 ModulePathHelper::ConcatFileNameWithMerge(thread, curJsPandaFile.get(),
187 fileName, recordNameStr, requestPath);
188 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
189 moduleName = entryPoint;
190 }
191 std::shared_ptr<JSPandaFile> jsPandaFile =
192 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, fileName, entryPoint, false, ExecuteTypes::STATIC);
193 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
194 if (jsPandaFile == nullptr) {
195 LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << fileName;
196 }
197
198 JSRecordInfo *recordInfo = jsPandaFile->CheckAndGetRecordInfo(entryPoint);
199 if (recordInfo == nullptr) {
200 CString normalizeStr = ModulePathHelper::ReformatPath(entryPoint);
201 CString msg = "Cannot find dynamic-import module '" + normalizeStr;
202 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), CatchException(thread, reject));
203 }
204
205 if (jsPandaFile->IsJson(recordInfo)) {
206 return DynamicImport::ExecuteNativeOrJsonModule(
207 thread, entryPoint, ModuleTypes::JSON_MODULE, resolve, reject, jsPandaFile.get());
208 }
209 // Loading request module.
210 thread->GetEcmaVM()->PushToDeregisterModuleList(entryPoint);
211 // IsInstantiatedModule is for lazy module to execute
212 if (!moduleManager->IsModuleLoaded(moduleName) || moduleManager->IsInstantiatedModule(moduleName)) {
213 if (!JSPandaFileExecutor::ExecuteFromAbcFile(
214 thread, fileName.c_str(), entryPoint.c_str(), false, ExecuteTypes::DYNAMIC)) {
215 CString msg = "Cannot execute request dynamic-imported module : " + entryPoint;
216 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), CatchException(thread, reject));
217 }
218 } else {
219 ModuleDeregister::ReviseLoadedModuleCount(thread, moduleName);
220 }
221
222 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
223 JSMutableHandle<JSTaggedValue> moduleNamespace(thread, JSTaggedValue::Undefined());
224 // only support importing es module, or return a default object.
225 if (!jsPandaFile->IsModule(recordInfo)) {
226 moduleNamespace.Update(vm->GetGlobalEnv()->GetExportOfScript());
227 } else {
228 // b. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
229 JSHandle<SourceTextModule> moduleRecord =
230 moduleManager->GetImportedModule(moduleName);
231 moduleRecord->CheckAndThrowModuleError(thread);
232 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
233 JSHandle<JSTaggedValue> nameSp = SourceTextModule::GetModuleNamespace(thread, moduleRecord);
234 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
235 // d. Let namespace be ? GetModuleNamespace(moduleRecord).
236 moduleNamespace.Update(nameSp);
237 }
238 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
239 EcmaRuntimeCallInfo *info =
240 EcmaInterpreter::NewRuntimeCallInfo(thread,
241 JSHandle<JSTaggedValue>(resolve),
242 undefined, undefined, 1);
243 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
244 info->SetCallArg(moduleNamespace.GetTaggedValue());
245 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
246 return JSFunction::Call(info);
247 }
248
CatchException(JSThread * thread,JSHandle<JSPromiseReactionsFunction> reject)249 JSTaggedValue BuiltinsPromiseJob::CatchException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> reject)
250 {
251 BUILTINS_API_TRACE(thread, PromiseJob, CatchException);
252 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
253 ASSERT(thread->HasPendingException());
254 JSHandle<JSTaggedValue> thenResult = JSPromise::IfThrowGetThrowValue(thread);
255 thread->ClearException();
256 JSHandle<JSTaggedValue> rejectfun(reject);
257 EcmaRuntimeCallInfo *runtimeInfo =
258 EcmaInterpreter::NewRuntimeCallInfo(thread, rejectfun, undefined, undefined, 1);
259 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
260 runtimeInfo->SetCallArg(thenResult.GetTaggedValue());
261 return JSFunction::Call(runtimeInfo);
262 }
263 } // namespace panda::ecmascript::builtins
264