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 27namespace panda::ecmascript::builtins { 28using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo; 29using ModulePathHelper = ecmascript::ModulePathHelper; 30using PathHelper = ecmascript::base::PathHelper; 31 32JSTaggedValue 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 87JSTaggedValue 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 126JSTaggedValue 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 254JSTaggedValue 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