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