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