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