/* * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/builtins/builtins_promise.h" #include "ecmascript/interpreter/fast_runtime_stub-inl.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jspandafile/js_pandafile_executor.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/module/js_shared_module_manager.h" #include "ecmascript/module/module_logger.h" #include "ecmascript/module/module_message_helper.h" #include "ecmascript/module/module_path_helper.h" #include "ecmascript/platform/file.h" #include "ecmascript/module/module_value_accessor.h" #include "ecmascript/module/module_resolver.h" #include "ecmascript/module/module_tools.h" #include "ecmascript/object_fast_operator-inl.h" #include "ecmascript/runtime_lock.h" #include "ecmascript/dfx/stackinfo/js_stackinfo.h" #include "ecmascript/patch/quick_fix_manager.h" namespace panda::ecmascript { using PathHelper = base::PathHelper; using StringHelper = base::StringHelper; using GlobalError = containers::ContainerError; CVector SourceTextModule::GetExportedNames(JSThread *thread, const JSHandle &module, const JSHandle &exportStarSet) { CVector exportedNames; // 1. Let module be this Source Text Module Record. // 2. If exportStarSet contains module, then if (exportStarSet->GetIdx(thread, module.GetTaggedValue()) != TaggedArray::MAX_ARRAY_INDEX) { // a. Assert: We've reached the starting point of an import * circularity. // b. Return a new empty List. return exportedNames; } // 3. Append module to exportStarSet. size_t len = exportStarSet->GetLength(); JSHandle newExportStarSet = TaggedArray::SetCapacity(thread, exportStarSet, len + 1); newExportStarSet->Set(thread, len, module.GetTaggedValue()); JSTaggedValue entryValue = module->GetLocalExportEntries(thread); // 5. For each ExportEntry Record e in module.[[LocalExportEntries]], do AddExportName(thread, entryValue, exportedNames); // 6. For each ExportEntry Record e in module.[[IndirectExportEntries]], do entryValue = module->GetIndirectExportEntries(thread); AddExportName(thread, entryValue, exportedNames); entryValue = module->GetStarExportEntries(thread); auto globalConstants = thread->GlobalConstants(); if (!entryValue.IsUndefined()) { JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSHandle requestedModules(thread, module->GetRequestedModules(thread)); // 7. For each ExportEntry Record e in module.[[StarExportEntries]], do JSHandle starExportEntries(thread, entryValue); size_t starExportEntriesLen = starExportEntries->GetLength(); for (size_t idx = 0; idx < starExportEntriesLen; idx++) { ee.Update(starExportEntries->Get(thread, idx)); // a. Let requestedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]). JSHandle requestedModule = GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, ee->GetModuleRequestIndex()); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exportedNames); ASSERT(requestedModule.GetTaggedValue().IsSourceTextModule()); SetExportName(thread, requestedModule, exportedNames, newExportStarSet); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exportedNames); } } return exportedNames; } bool SourceTextModule::CheckCircularImport(JSThread *thread, const JSHandle &module, const JSHandle &exportName, ResolvedMultiMap &resolvedMap) { if (resolvedMap.empty()) { return false; } auto range = resolvedMap.equal_range(reinterpret_cast(GetModuleName(module))); for (auto iter = range.first; iter != range.second; ++iter) { auto name = iter->second; // a. If module and r.[[Module]] are the same Module Record and // SameValue(exportName, r.[[ExportName]]) is true, then if (JSTaggedValue::StringCompare(thread, EcmaString::Cast(name.GetTaggedValue().GetTaggedObject()), EcmaString::Cast(exportName.GetTaggedValue().GetTaggedObject()))) { // i. Assert: This is a circular import request. // ii. Return true. return true; } } return false; } JSHandle SourceTextModule::GetBindingNameByIndex(JSThread *thread, const JSHandle module, const int index) { auto globalConstants = thread->GlobalConstants(); if (index == SourceTextModule::UNDEFINED_INDEX) { return globalConstants->GetHandledDefaultString(); } JSHandle exports = ModuleValueAccessor::GetNativeOrCjsExports(thread, module.GetTaggedValue()); if (exports->IsJSObject()) { JSObject *exportObject = JSObject::Cast(exports.GetTaggedValue().GetTaggedObject()); TaggedArray *properties = TaggedArray::Cast(exportObject->GetProperties(thread).GetTaggedObject()); if (!properties->IsDictionaryMode()) { JSHandle jsHClass(thread, exportObject->GetJSHClass()); // Get layoutInfo and compare the input and output names of files LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHClass->GetLayout(thread).GetTaggedObject()); if (layoutInfo->NumberOfElements() > index) { JSHandle key(thread, layoutInfo->GetKey(thread, index)); return key; } } else { NameDictionary *dict = NameDictionary::Cast(properties); if (dict->Size() > index) { JSHandle key(thread, dict->GetKey(thread, index)); return key; } } } return globalConstants->GetHandledUndefined(); } JSHandle SourceTextModule::ResolveExportObject(JSThread *thread, const JSHandle &module, const JSHandle &exports, const JSHandle &exportName) { // Let module be this Source Text Module Record. auto globalConstants = thread->GlobalConstants(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // For CJS, if exports is not JSObject, means the CJS module use default output JSHandle defaultString = globalConstants->GetHandledDefaultString(); if (JSTaggedValue::SameValueString(thread, exportName, defaultString)) { // bind with a number return JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, -1)); } if (exports->IsNativeModuleFailureInfo()) { return JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, -1)); } if (exports->IsJSObject()) { JSHandle resolution(thread, JSTaggedValue::Hole()); JSObject *exportObject = JSObject::Cast(exports.GetTaggedValue().GetTaggedObject()); TaggedArray *properties = TaggedArray::Cast(exportObject->GetProperties(thread).GetTaggedObject()); if (!properties->IsDictionaryMode()) { JSHandle jsHclass(thread, exportObject->GetJSHClass()); // Get layoutInfo and compare the input and output names of files LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout(thread).GetTaggedObject()); if (layoutInfo->NumberOfElements() != 0) { resolution = ResolveElementOfObject(thread, jsHclass, exportName, module); } } else { NameDictionary *dict = NameDictionary::Cast(properties); int entry = dict->FindEntry(thread, exportName.GetTaggedValue()); if (entry != -1) { resolution = JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, entry)); } } if (!resolution->IsUndefined()) { return resolution; } } return globalConstants->GetHandledNull(); } JSHandle SourceTextModule::ResolveNativeStarExport(JSThread *thread, const JSHandle &nativeModule, const JSHandle &exportName) { ModuleTypes moduleType = nativeModule->GetTypes(); if (!SourceTextModule::EvaluateNativeModule(thread, nativeModule, moduleType)) { return thread->GlobalConstants()->GetHandledNull(); } JSHandle nativeExports(thread, nativeModule->GetModuleValue(thread, 0, false)); return SourceTextModule::ResolveExportObject(thread, nativeModule, nativeExports, exportName); } JSHandle SourceTextModule::ResolveCjsStarExport(JSThread *thread, const JSHandle &cjsModule, const JSHandle &exportName) { if (cjsModule->GetStatus() < ModuleStatus::EVALUATED) { SourceTextModule::ModuleExecution(thread, cjsModule); SourceTextModule::RecordEvaluatedOrError(thread, cjsModule); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, thread->GlobalConstants()->GetHandledNull()); } CString moduleName = GetModuleName(cjsModule.GetTaggedValue()); JSHandle cjsModuleName(thread->GetEcmaVM()->GetFactory()->NewFromUtf8(moduleName)); JSHandle cjsExports = CjsModule::SearchFromModuleCache(thread, cjsModuleName); return SourceTextModule::ResolveExportObject(thread, cjsModule, cjsExports, exportName); } JSHandle SourceTextModule::ResolveExport(JSThread *thread, const JSHandle &module, const JSHandle &exportName, ResolvedMultiMap &resolvedMap) { // 1. Let module be this Source Text Module Record. auto globalConstants = thread->GlobalConstants(); // Check if circular import request. // 2.For each Record { [[Module]], [[ExportName]] } r in resolvedMap, do if (CheckCircularImport(thread, module, exportName, resolvedMap)) { return globalConstants->GetHandledNull(); } // 3. Append the Record { [[Module]]: module, [[ExportName]]: exportName } to resolvedMap. resolvedMap.emplace(reinterpret_cast(GetModuleName(module)), exportName); // 4. For each ExportEntry Record e in module.[[LocalExportEntries]], do JSHandle localExportEntriesTv(thread, module->GetLocalExportEntries(thread)); if (!localExportEntriesTv->IsUndefined()) { JSHandle resolution = ResolveLocalExport(thread, localExportEntriesTv, exportName, module); if (!resolution->IsUndefined()) { return resolution; } } // 5. For each ExportEntry Record e in module.[[IndirectExportEntries]], do JSHandle indirectExportEntriesTv(thread, module->GetIndirectExportEntries(thread)); if (!indirectExportEntriesTv->IsUndefined()) { JSHandle resolution = ResolveIndirectExport(thread, indirectExportEntriesTv, exportName, module, resolvedMap); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); if (!resolution->IsUndefined()) { return resolution; } } // 6. If SameValue(exportName, "default") is true, then JSHandle defaultString = globalConstants->GetHandledDefaultString(); if (JSTaggedValue::SameValueString(thread, exportName, defaultString)) { // a. Assert: A default export was not explicitly defined by this module. // b. Return null. // c. NOTE: A default export cannot be provided by an export *. return globalConstants->GetHandledNull(); } // 7. Let starResolution be null. JSMutableHandle starResolution(thread, globalConstants->GetNull()); // 8. For each ExportEntry Record e in module.[[StarExportEntries]], do JSTaggedValue starExportEntriesTv = module->GetStarExportEntries(thread); if (starExportEntriesTv.IsUndefined()) { return starResolution; } JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSHandle requestedModules(thread, module->GetRequestedModules(thread)); JSHandle starExportEntries(thread, starExportEntriesTv); size_t starExportEntriesLen = starExportEntries->GetLength(); for (size_t idx = 0; idx < starExportEntriesLen; idx++) { ee.Update(starExportEntries->Get(thread, idx)); // a. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]). JSHandle requestedModule = GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, ee->GetModuleRequestIndex()); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); ASSERT(requestedModule.GetTaggedValue().IsSourceTextModule()); JSHandle result = GetStarResolution(thread, exportName, requestedModule, starResolution, resolvedMap); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); if (result->IsString() || result->IsException()) { return result; } } // 9. Return starResolution. return starResolution; } bool SourceTextModule::IsNativeModule(const CString &moduleRequestName) { if (moduleRequestName[0] != '@' || StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::PREFIX_BUNDLE) || StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::PREFIX_PACKAGE) || StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::PREFIX_NORMALIZED_NOT_SO) || moduleRequestName.find(':') == CString::npos) { return false; } return true; } ModuleTypes SourceTextModule::GetNativeModuleType(const CString &moduleRequestName) { if (StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::REQUIRE_NAPI_OHOS_PREFIX)) { return ModuleTypes::OHOS_MODULE; } /* * moduleRequestName: @app:xxx/xxx * : @normalized:Y&xxx */ if (StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::REQUIRE_NAPI_APP_PREFIX) || StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::PREFIX_NORMALIZED_SO)) { return ModuleTypes::APP_MODULE; } if (StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::REQUIRE_NAITVE_MODULE_PREFIX)) { return ModuleTypes::NATIVE_MODULE; } return ModuleTypes::INTERNAL_MODULE; } JSHandle SourceTextModule::GetRequireNativeModuleFunc(EcmaVM *vm, ModuleTypes moduleType) { auto globalConstants = vm->GetJSThread()->GlobalConstants(); auto undefined = globalConstants->GetHandledUndefined(); auto funcName = (moduleType == ModuleTypes::NATIVE_MODULE) ? globalConstants->GetRequireNativeModuleString() : globalConstants->GetRequireNapiString(); JSHandle globalEnv = vm->GetGlobalEnv(); CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, undefined); ThreadManagedScope managedScope(vm->GetJSThread()); JSTaggedValue func = ObjectFastOperator::FastGetPropertyByValue(vm->GetJSThread(), globalEnv->GetGlobalObject(), funcName); RETURN_VALUE_IF_ABRUPT(thread, undefined); return JSHandle(vm->GetJSThread(), func); } EcmaRuntimeCallInfo* SourceTextModule::MakeNormalizedAppArgs(const EcmaVM *vm, JSHandle func, const CString &soPath, const CString &moduleName) { JSThread *thread = vm->GetJSThread(); const GlobalEnvConstants *globalConstants = thread->GlobalConstants(); ObjectFactory *factory = vm->GetFactory(); JSHandle undefined = globalConstants->GetHandledUndefined(); CString soName = ModulePathHelper::GetNormalizedPathFromOhmUrl(soPath); CString path = base::ConcatToCString(ModulePathHelper::GetBundleNameFromNormalized(vm, soPath), PathHelper::SLASH_TAG, moduleName); // use module name as so name EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, undefined, 3); RETURN_VALUE_IF_ABRUPT(thread, nullptr); JSHandle arg0 = factory->NewFromUtf8(soName.c_str()); JSHandle arg2 = factory->NewFromUtf8(path.c_str()); info->SetCallArg(arg0.GetTaggedValue(), globalConstants->GetTrue(), arg2.GetTaggedValue()); return info; } EcmaRuntimeCallInfo* SourceTextModule::MakeAppArgs(const EcmaVM *vm, JSHandle func, const CString &soPath, const CString &moduleName, const CString &requestName) { if (!StringHelper::StringStartWith(requestName, ModulePathHelper::REQUIRE_NAPI_APP_PREFIX)) { return MakeNormalizedAppArgs(vm, func, soPath, moduleName); } JSThread *thread = vm->GetJSThread(); const GlobalEnvConstants *globalConstants = thread->GlobalConstants(); ObjectFactory *factory = vm->GetFactory(); JSHandle undefined = globalConstants->GetHandledUndefined(); size_t pos = soPath.find_last_of(PathHelper::SLASH_TAG); if (pos == CString::npos) { // LCOV_EXCL_BR_LINE LOG_FULL(FATAL) << "Invalid native module " << soPath; UNREACHABLE(); } CString soName = soPath.substr(pos + 1); CString path = soPath.substr(0, pos); // use module name as so name EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, undefined, 3); RETURN_VALUE_IF_ABRUPT(thread, nullptr); JSHandle arg0 = factory->NewFromUtf8(soName.c_str()); JSHandle arg2 = factory->NewFromUtf8(path.c_str()); info->SetCallArg(arg0.GetTaggedValue(), globalConstants->GetTrue(), arg2.GetTaggedValue()); return info; } EcmaRuntimeCallInfo* SourceTextModule::MakeInternalArgs(const EcmaVM *vm, JSHandle func, const CString &soPath, const CString &moduleRequestName) { JSThread* thread = vm->GetJSThread(); const GlobalEnvConstants* globalConstants = thread->GlobalConstants(); ObjectFactory* factory = vm->GetFactory(); JSHandle undefined = globalConstants->GetHandledUndefined(); CString moduleDir = PathHelper::GetInternalModulePrefix(moduleRequestName); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, undefined, 4); RETURN_VALUE_IF_ABRUPT(thread, nullptr); JSHandle arg0 = factory->NewFromUtf8(soPath.c_str()); JSHandle arg3 = factory->NewFromUtf8(moduleDir.c_str()); info->SetCallArg(arg0.GetTaggedValue(), globalConstants->GetFalse(), globalConstants->GetEmptyString(), arg3.GetTaggedValue()); return info; } JSHandle SourceTextModule::LoadNativeModuleCallFunc(EcmaVM *vm, EcmaRuntimeCallInfo* info) { auto undefined = vm->GetJSThread()->GlobalConstants()->GetHandledUndefined(); CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, undefined); ThreadManagedScope managedScope(thread); FunctionCallScope callScope(EcmaVM::ConstCast(vm)); vm->GetJsDebuggerManager()->ClearSingleStepper(); JSTaggedValue result = JSFunction::Call(info); #if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK thread->CheckJSTaggedType(result.GetRawData()); #endif if (thread->HasPendingException()) { JsStackInfo::BuildCrashInfo(thread); } RETURN_VALUE_IF_ABRUPT(thread, undefined); JSHandle resultValue(thread, result); EcmaVM::ClearKeptObjects(thread); vm->GetJsDebuggerManager()->NotifyReturnNative(); return resultValue; } JSHandle SourceTextModule::LoadNativeModuleImpl(EcmaVM *vm, JSThread *thread, const JSHandle &requiredModule, ModuleTypes moduleType) { CString moduleRequestName = requiredModule->GetEcmaModuleRecordNameString(); ModuleTraceScope moduleTraceScope(thread, "SourceTextModule::LoadNativeModule:" + moduleRequestName); ModuleLogger *moduleLogger = thread->GetModuleLogger(); auto undefined = vm->GetJSThread()->GlobalConstants()->GetHandledUndefined(); if (moduleLogger != nullptr) { moduleLogger->SetStartTime(moduleRequestName); } CString soName = PathHelper::GetStrippedModuleName(moduleRequestName); CString fileName = requiredModule->GetEcmaModuleFilenameString(); CString moduleName = ModulePathHelper::GetModuleNameWithBaseFile(fileName); LOG_FULL(DEBUG) << "Request module is " << moduleRequestName; JSHandle func = GetRequireNativeModuleFunc(vm, moduleType); if (!func->IsCallable()) { LOG_FULL(WARN) << "Not found require func"; if (moduleLogger != nullptr) { moduleLogger->SetEndTime(moduleRequestName); } return undefined; } EcmaRuntimeCallInfo* info; if (moduleType == ModuleTypes::APP_MODULE) { info = MakeAppArgs(vm, func, soName, moduleName, moduleRequestName); } else if (moduleType == ModuleTypes::INTERNAL_MODULE) { info = MakeInternalArgs(vm, func, soName, moduleRequestName); } else { info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, undefined, 1); info->SetCallArg(0, vm->GetFactory()->NewFromUtf8(soName.c_str()).GetTaggedValue()); } RETURN_VALUE_IF_ABRUPT(thread, undefined); // Consistent with FunctionRef::Call JSHandle exportObject = LoadNativeModuleCallFunc(vm, info); if (moduleLogger != nullptr) { moduleLogger->SetEndTime(moduleRequestName); } RETURN_VALUE_IF_ABRUPT(thread, undefined); return exportObject; } JSHandle SourceTextModule::LoadNativeModuleMayThrowError(JSThread *thread, const JSHandle &requiredModule, ModuleTypes moduleType) { EcmaVM *vm = thread->GetEcmaVM(); [[maybe_unused]] EcmaHandleScope handleScope(thread); auto exportObject = LoadNativeModuleImpl(vm, thread, requiredModule, moduleType); if (exportObject->IsNativeModuleFailureInfo() || exportObject->IsUndefined()) { CString errorMsg = "load native module failed."; LOG_FULL(ERROR) << errorMsg.c_str(); auto error = GlobalError::ReferenceError(thread, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, thread->GlobalConstants()->GetHandledUndefined()); } return exportObject; } bool SourceTextModule::LoadNativeModule(JSThread *thread, const JSHandle &requiredModule, ModuleTypes moduleType) { EcmaVM *vm = thread->GetEcmaVM(); [[maybe_unused]] EcmaHandleScope handleScope(thread); auto exportObject = LoadNativeModuleImpl(vm, thread, requiredModule, moduleType); CString moduleName = requiredModule->GetEcmaModuleRecordNameString(); if (UNLIKELY(exportObject->IsUndefined())) { LOG_FULL(ERROR) << "export objects of native so is undefined, so name is " << moduleName; return false; } if (UNLIKELY(exportObject->IsNativeModuleFailureInfo())) { SourceTextModule::StoreModuleValue(thread, requiredModule, 0, exportObject); LOG_FULL(ERROR) << "loading fails, NativeModuleErrorObject is returned, so name is " << moduleName; return false; } ASSERT(!thread->HasPendingException()); SourceTextModule::StoreModuleValue(thread, requiredModule, 0, exportObject); return true; } bool SourceTextModule::EvaluateNativeModule(JSThread *thread, JSHandle nativeModule, ModuleTypes moduleType) { if (nativeModule->GetStatus() == ModuleStatus::EVALUATED) { return true; } if (!SourceTextModule::LoadNativeModule(thread, nativeModule, moduleType)) { LOG_FULL(INFO) << "LoadNativeModule " << nativeModule->GetEcmaModuleRecordNameString() << " failed"; // LATER DO: add error status. return false; } nativeModule->SetStatus(ModuleStatus::EVALUATED); return true; } int SourceTextModule::HandleInstantiateException([[maybe_unused]] JSHandle &module, const CVector> &stack, int result) { // a. For each module m in stack, do for (auto mm : stack) { // i. Assert: m.[[Status]] is "instantiating". ASSERT(mm->GetStatus() == ModuleStatus::INSTANTIATING); // ii. Set m.[[Status]] to "uninstantiated". mm->SetStatus(ModuleStatus::UNINSTANTIATED); // iii. Set m.[[Environment]] to undefined. // iv. Set m.[[DFSIndex]] to undefined. mm->SetDFSIndex(SourceTextModule::UNDEFINED_INDEX); // v. Set m.[[DFSAncestorIndex]] to undefined. mm->SetDFSAncestorIndex(SourceTextModule::UNDEFINED_INDEX); } // b. Assert: module.[[Status]] is "uninstantiated". ASSERT(module->GetStatus() == ModuleStatus::UNINSTANTIATED); // c. return result return result; } int SourceTextModule::Instantiate(JSThread *thread, const JSHandle &moduleHdl, const ExecuteTypes &executeType) { STACK_LIMIT_CHECK(thread, SourceTextModule::UNDEFINED_INDEX); ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SourceTextModule::Instantiate", ""); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX); JSHandle module = JSHandle::Cast(moduleHdl); // 1. Let module be this Source Text Module Record. // 2. Assert: module.[[Status]] is one of UNLINKED, LINKED, EVALUATING-ASYNC, or EVALUATED. ModuleStatus status = module->GetStatus(); ASSERT(status == ModuleStatus::UNINSTANTIATED || status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED || status == ModuleStatus::ERRORED); // 4. Let result be InnerModuleInstantiation(module, stack, 0). SourceTextModule::PreModuleInstantiation(thread, module, executeType); JSHandle exception; // 5. If result is an abrupt completion, then if (thread->HasPendingException()) { // handle exception here may cause incompatible changes, therefore, resolve module failed still need to bind. // clear exception here, save the exception, handle exception in FinishModuleInstantiation process. exception = JSHandle(thread, thread->GetException()); thread->ClearException(); } // 3. Let stack be a new empty List. CVector> stack; int result = FinishModuleInstantiation(thread, module, stack, 0, exception); if (thread->HasPendingException()) { return HandleInstantiateException(module, stack, result); } // 6. Assert: module.[[Status]] is one of LINKED, EVALUATING-ASYNC, or EVALUATED. status = module->GetStatus(); ASSERT(status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED || status == ModuleStatus::ERRORED); // 7. Assert: stack is empty. ASSERT(stack.empty()); // 8. Return undefined. return SourceTextModule::UNDEFINED_INDEX; } std::optional> SourceTextModule::GetConcurrentRequestedModules(JSThread *thread, const JSHandle &method) { const JSPandaFile *jsPandaFile = method->GetJSPandaFile(thread); const MethodLiteral *methodLiteral = method->GetMethodLiteral(thread); ASSERT(methodLiteral != nullptr); return methodLiteral->GetConcurrentRequestedModules(jsPandaFile); } void SourceTextModule::DFSModuleInstantiation(JSThread *thread, JSHandle &module, CVector> &stack) { // 1. Assert: module occurs exactly once in stack. // 2. Assert: module.[[DFSAncestorIndex]] is less than or equal to module.[[DFSIndex]]. int dfsAncIdx = module->GetDFSAncestorIndex(); int dfsIdx = module->GetDFSIndex(); ASSERT(dfsAncIdx <= dfsIdx); // 3. If module.[[DFSAncestorIndex]] equals module.[[DFSIndex]], then if (dfsAncIdx == dfsIdx) { // a. Let done be false. bool done = false; // b. Repeat, while done is false, while (!done) { // i. Let requiredModule be the last element in stack. JSHandle requiredModule = stack.back(); // ii. Remove the last element of stack. stack.pop_back(); // iii. Set requiredModule.[[Status]] to "instantiated". requiredModule->SetStatus(ModuleStatus::INSTANTIATED); // iv. If requiredModule and module are the same Module Record, set done to true. if (JSTaggedValue::SameValue(thread, module.GetTaggedValue(), requiredModule.GetTaggedValue())) { done = true; } } } } bool SourceTextModule::PreModuleInstantiation(JSThread *thread, JSHandle module, const ExecuteTypes &executeType) { // Add a safepoint here to check if a suspension is needed. thread->CheckSafepointIfSuspended(); ModuleStatus status = module->GetStatus(); ASSERT(status != ModuleStatus::INSTANTIATING); if (status != ModuleStatus::UNINSTANTIATED && status != ModuleStatus::EVALUATING) { return true; } bool isShared = SourceTextModule::IsSharedModule(module); if (isShared && status == ModuleStatus::EVALUATING) { LOG_FULL(INFO) << "circular dependency occurred of shared-module"; return true; } module->SetStatus(ModuleStatus::PREINSTANTIATING); JSHandle moduleRequests(thread, module->GetModuleRequests(thread)); // 9. For each String required that is an element of module.[[RequestedModules]], do if (!moduleRequests.GetTaggedValue().IsUndefined()) { JSHandle requestedModules(thread, module->GetRequestedModules(thread)); size_t moduleRequestsLen = moduleRequests->GetLength(); JSMutableHandle required(thread, thread->GlobalConstants()->GetUndefined()); for (size_t idx = 0; idx < moduleRequestsLen; idx++) { required.Update(moduleRequests->Get(thread, idx)); JSHandle requiredModule = ModuleResolver::HostResolveImportedModule(thread, module, required, executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); SetRequestedModules(thread, requestedModules, idx, requiredModule, isShared); PreModuleInstantiation(thread, JSHandle::Cast(requiredModule), executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); } } return true; } int SourceTextModule::FinishModuleInstantiation(JSThread *thread, JSHandle module, CVector> &stack, int index, JSHandle exception) { // Add a safepoint here to check if a suspension is needed. thread->CheckSafepointIfSuspended(); // ArkTS module doesn't implement other module Record, delete follow branch. // 1. If module is not a Source Text Module Record, then // a. Perform ? module.Instantiate(). // b. Return index. // 2. If module.[[Status]] is one of LINKING, LINKED, EVALUATING-ASYNC, or EVALUATED, then Return index. ModuleStatus status = module->GetStatus(); if (status >= ModuleStatus::INSTANTIATING) { return index; } // 3. Assert: module.[[Status]] is "PREINSTANTIATING". ASSERT(status == ModuleStatus::PREINSTANTIATING); // 4. Set module.[[Status]] to "instantiating". module->SetStatus(ModuleStatus::INSTANTIATING); // 5. Set module.[[DFSIndex]] to index. module->SetDFSIndex(index); // 6. Set module.[[DFSAncestorIndex]] to index. module->SetDFSAncestorIndex(index); // 7. Set index to index + 1. index++; // 8. Append module to stack. stack.emplace_back(module); // 9. For each String required that is an element of module.[[RequestedModules]], do if (!module->GetRequestedModules(thread).IsUndefined()) { JSHandle requestedModules(thread, module->GetRequestedModules(thread)); size_t requestedModulesLen = requestedModules->GetLength(); for (size_t idx = 0; idx < requestedModulesLen; idx++) { JSHandle moduleHdl = GetRequestedModuleMayThrowError(thread, module, idx, requestedModules, exception); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); JSHandle requiredModule = JSHandle::Cast(moduleHdl); index = FinishModuleInstantiation(thread, requiredModule, stack, index, exception); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // c. Assert: requiredModule.[[Status]] is one of LINKING, LINKED, EVALUATING-ASYNC, or EVALUATED. ModuleStatus requiredModuleStatus = requiredModule->GetStatus(); ASSERT(requiredModuleStatus >= ModuleStatus::INSTANTIATING && requiredModuleStatus != ModuleStatus::EVALUATING); // d. Assert: requiredModule.[[Status]] is "instantiating" if and only if requiredModule is in stack. // e. If requiredModule.[[Status]] is "instantiating", then if (requiredModuleStatus == ModuleStatus::INSTANTIATING) { // d. Assert: requiredModule.[[Status]] is "instantiating" if and only if requiredModule is in stack. ASSERT(std::find(stack.begin(), stack.end(), requiredModule) != stack.end()); // i. Assert: requiredModule is a Source Text Module Record. // ii. Set module.[[DFSAncestorIndex]] to min( // module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]). int dfsAncIdx = std::min(module->GetDFSAncestorIndex(), requiredModule->GetDFSAncestorIndex()); module->SetDFSAncestorIndex(dfsAncIdx); } } } // Adapter new opcode // 10. Perform ? ModuleDeclarationEnvironmentSetup(module). if (module->GetIsNewBcVersion()) { SourceTextModule::ModuleDeclarationArrayEnvironmentSetup(thread, module); } else { SourceTextModule::ModuleDeclarationEnvironmentSetup(thread, module); } RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); DFSModuleInstantiation(thread, module, stack); return index; } void SourceTextModule::ModuleDeclarationEnvironmentSetup(JSThread *thread, const JSHandle &module) { CheckResolvedBinding(thread, module); if (module->GetImportEntries(thread).IsUndefined()) { return; } ASSERT(!SourceTextModule::IsSharedModule(module)); // 2. Assert: All named exports from module are resolvable. // 3. Let realm be module.[[Realm]]. // 4. Assert: realm is not undefined. // 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). JSHandle importEntries(thread, module->GetImportEntries(thread)); size_t importEntriesLen = importEntries->GetLength(); JSHandle map(NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(importEntriesLen))); // 6. Set module.[[Environment]] to env. module->SetEnvironment(thread, map); // 7. Let envRec be env's EnvironmentRecord. JSMutableHandle envRec(thread, module->GetEnvironment(thread)); ASSERT(!envRec->IsUndefined()); // 8. For each ImportEntry Record in in module.[[ImportEntries]], do JSHandle requestedModules(thread, module->GetRequestedModules(thread)); auto globalConstants = thread->GlobalConstants(); JSMutableHandle in(thread, globalConstants->GetUndefined()); JSMutableHandle importName(thread, globalConstants->GetUndefined()); JSMutableHandle localName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < importEntriesLen; idx++) { in.Update(importEntries->Get(thread, idx)); localName.Update(in->GetLocalName(thread)); importName.Update(in->GetImportName(thread)); // a. Let importedModule be ! HostResolveImportedModule(module, in.[[ModuleRequest]]). JSHandle importedModule = JSHandle::Cast( GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, in->GetModuleRequestIndex())); RETURN_IF_ABRUPT_COMPLETION(thread); ASSERT(importedModule.GetTaggedValue().IsSourceTextModule()); // c. If in.[[ImportName]] is "*", then JSHandle starString = globalConstants->GetHandledStarString(); if (JSTaggedValue::SameValueString(thread, importName, starString)) { // i. Let namespace be ? GetModuleNamespace(importedModule). JSHandle moduleNamespace = SourceTextModule::GetModuleNamespace(thread, importedModule); // ii. Perform ! envRec.CreateImmutableBinding(in.[[LocalName]], true). // iii. Call envRec.InitializeBinding(in.[[LocalName]], namespace). JSHandle mapHandle = JSHandle::Cast(envRec); JSHandle newMap = NameDictionary::Put(thread, mapHandle, localName, moduleNamespace, PropertyAttributes::Default()); envRec.Update(newMap); } else { // i. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]], « »). ResolvedMultiMap resolvedMap; JSHandle resolution = SourceTextModule::ResolveExport(thread, importedModule, importName, resolvedMap); RETURN_IF_ABRUPT_COMPLETION(thread); // ii. If resolution is null or "ambiguous", throw a SyntaxError exception. if (resolution->IsNull() || resolution->IsString()) { CString requestMod = ModulePathHelper::ReformatPath(GetModuleName(importedModule.GetTaggedValue())); CString msg = "the requested module '" + requestMod + GetResolveErrorReason(resolution) + ConvertToString(thread, importName.GetTaggedValue()); if (!module->GetEcmaModuleRecordNameString().empty()) { CString recordStr = ModulePathHelper::ReformatPath(module->GetEcmaModuleRecordNameString()); msg += "' which imported by '" + recordStr + "'"; } else { msg += "' which imported by '" + module->GetEcmaModuleFilenameString() + "'"; } THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } // iii. Call envRec.CreateImportBinding( // in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]). JSHandle mapHandle = JSHandle::Cast(envRec); JSHandle newMap = NameDictionary::Put(thread, mapHandle, localName, resolution, PropertyAttributes::Default()); envRec.Update(newMap); } } module->SetEnvironment(thread, envRec); } void SourceTextModule::ModuleDeclarationArrayEnvironmentSetup(JSThread *thread, const JSHandle &module) { ModuleTraceScope moduleTraceScope(thread, "SourceTextModule::Instantiating:" + module->GetEcmaModuleRecordNameString()); if (IsSharedModule(module) && SharedModuleManager::GetInstance()->IsInstantiatedSModule(thread, module)) { return; } CheckResolvedIndexBinding(thread, module); if (module->GetImportEntries(thread).IsUndefined()) { return; } ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 2. Assert: All named exports from module are resolvable. // 3. Let realm be module.[[Realm]]. // 4. Assert: realm is not undefined. // 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). JSHandle importEntries(thread, module->GetImportEntries(thread)); size_t importEntriesLen = importEntries->GetLength(); JSHandle arr = factory->NewTaggedArray(importEntriesLen); // 7. Let envRec be env's EnvironmentRecord. JSHandle envRec = arr; // 8. For each ImportEntry Record in in module.[[ImportEntries]], do JSHandle requestedModules(thread, module->GetRequestedModules(thread)); auto globalConstants = thread->GlobalConstants(); JSMutableHandle in(thread, globalConstants->GetUndefined()); JSMutableHandle importName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < importEntriesLen; idx++) { in.Update(importEntries->Get(thread, idx)); importName.Update(in->GetImportName(thread)); // a. Let importedModule be ! HostResolveImportedModule(module, in.[[ModuleRequest]]). JSHandle importedModule = GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, in->GetModuleRequestIndex()); RETURN_IF_ABRUPT_COMPLETION(thread); ASSERT(importedModule.GetTaggedValue().IsSourceTextModule()); // c. If in.[[ImportName]] is "*", then JSHandle starString = globalConstants->GetHandledStarString(); if (JSTaggedValue::SameValueString(thread, importName, starString)) { // need refactor envRec = JSSharedModule::CloneEnvForSModule(thread, module, envRec); module->SetEnvironment(thread, envRec); return; } // i. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]], « »). ResolvedMultiMap resolvedMap; JSHandle resolution = SourceTextModule::ResolveExport(thread, importedModule, importName, resolvedMap); RETURN_IF_ABRUPT_COMPLETION(thread); // ii. If resolution is null or "ambiguous", throw a SyntaxError exception. if (resolution->IsNull() || resolution->IsString()) { CString requestMod = ModulePathHelper::ReformatPath(GetModuleName(importedModule.GetTaggedValue())); CString msg = "the requested module '" + requestMod + GetResolveErrorReason(resolution) + ConvertToString(thread, importName.GetTaggedValue()); if (!module->GetEcmaModuleRecordNameString().empty()) { CString recordStr = ModulePathHelper::ReformatPath( module->GetEcmaModuleRecordNameString()); msg += "' which imported by '" + recordStr + "'"; } else { msg += "' which imported by '" + module->GetEcmaModuleFilenameString() + "'"; } THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } // iii. Call envRec.CreateImportBinding( // in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]). envRec->Set(thread, idx, resolution); } envRec = JSSharedModule::CloneEnvForSModule(thread, module, envRec); module->SetEnvironment(thread, envRec); } JSHandle SourceTextModule::GetModuleNamespace(JSThread *thread, const JSHandle &module) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 1. Assert: module is an instance of a concrete subclass of Module Record. // 2. Assert: module.[[Status]] is not "uninstantiated". ASSERT(module->GetStatus() != ModuleStatus::UNINSTANTIATED); // 3. Let namespace be module.[[Namespace]]. JSMutableHandle moduleNamespace(thread, module->GetNamespace(thread).GetWeakRawValue()); // If namespace is undefined, then if (moduleNamespace->IsUndefined()) { // a. Let exportedNames be ? module.GetExportedNames(« »). JSHandle exportStarSet = factory->EmptyArray(); CVector exportedNames = SourceTextModule::GetExportedNames(thread, module, exportStarSet); // b. Let unambiguousNames be a new empty List. JSHandle unambiguousNames = factory->NewTaggedArray(exportedNames.size()); // c. For each name that is an element of exportedNames, do size_t idx = 0; for (std::string &name : exportedNames) { // i. Let resolution be ? module.ResolveExport(name, « »). ResolvedMultiMap resolvedMap; JSHandle nameHandle = JSHandle::Cast(factory->NewFromStdString(name)); JSHandle resolution = SourceTextModule::ResolveExport(thread, module, nameHandle, resolvedMap); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); // ii. If resolution is a ResolvedBinding Record, append name to unambiguousNames. if (resolution->IsModuleBinding()) { unambiguousNames->Set(thread, idx, nameHandle); idx++; } } JSHandle fixUnambiguousNames = TaggedArray::SetCapacity(thread, unambiguousNames, idx); JSHandle moduleTagged = JSHandle::Cast(module); JSHandle np = ModuleNamespace::ModuleNamespaceCreate(thread, moduleTagged, fixUnambiguousNames); moduleNamespace.Update(np.GetTaggedValue()); } return moduleNamespace; } void SourceTextModule::HandleEvaluateResult(JSThread *thread, JSHandle &module, JSHandle &capability, const CVector> &stack, const CVector> &errorStack) { ModuleStatus status; // 9. If result is an abrupt completion, then if (thread->HasPendingException()) { JSHandle exception(thread, thread->GetException()); HandleEvaluateException(thread, stack, exception); // b. Assert: module.[[Status]] is "evaluated" and module.[[EvaluationError]] is result. //d. Perform ! Call(capability.[[Reject]], undefined, « result.[[Value]] »). JSPromise::RejectPromise(thread, capability, exception); RETURN_IF_ABRUPT_COMPLETION(thread); return; } if (!errorStack.empty()) { return HandleErrorStack(thread, errorStack); } // a. Assert: module.[[Status]] is either EVALUATING-ASYNC or EVALUATED. status = module->GetStatus(); ASSERT(status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED); // b. Assert: module.[[EvaluationError]] is EMPTY. ASSERT(status != ModuleStatus::ERRORED); // c. If module.[[AsyncEvaluation]] is false, then // i. Assert: module.[[Status]] is EVALUATED || ERRORED. // ii. Perform ! Call(capability.[[Resolve]], undefined, « undefined »). if (!module->IsAsyncEvaluating()) { ASSERT(status >= ModuleStatus::EVALUATED); } // d. Assert: stack is empty. ASSERT(stack.empty()); } JSTaggedValue SourceTextModule::Evaluate(JSThread *thread, const JSHandle &moduleHdl, const void *buffer, size_t size, const ExecuteTypes &executeType) { ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SourceTextModule::Evaluate", ""); // 1. Let module be this Source Text Module Record. // 2. Assert: module.[[Status]] is one of LINKED, EVALUATING-ASYNC, or EVALUATED. JSMutableHandle module(thread, moduleHdl); ModuleStatus status = module->GetStatus(); ASSERT((status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED)); // 3. If module.[[Status]] is either EVALUATING-ASYNC or EVALUATED, set module to module.[[CycleRoot]]. if (status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED) { module.Update(module->GetCycleRoot(thread)); } // 4. If module.[[TopLevelCapability]] is not EMPTY, then // a. Return module.[[TopLevelCapability]].[[Promise]]. // 5. Let stack be a new empty List. CVector> stack; CVector> errorStack; // 6. Let capability be ! NewPromiseCapability(%Promise%). ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle capability = factory->NewJSPromise(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 7. Set module.[[TopLevelCapability]] to capability. if (!SourceTextModule::IsSharedModule(module)) { module->SetTopLevelCapability(thread, capability); } // 8. Let result be Completion(InnerModuleEvaluation(module, stack, 0)). SourceTextModule::InnerModuleEvaluation(thread, module, stack, errorStack, 0, buffer, size, executeType); HandleEvaluateResult(thread, module, capability, stack, errorStack); if (!thread->HasPendingException() && IsStaticImport(executeType)) { job::MicroJobQueue::ExecutePendingJob(thread, thread->GetEcmaVM()->GetMicroJobQueue()); } ModuleLogger *moduleLogger = thread->GetModuleLogger(); if ((moduleLogger != nullptr) && IsStaticImport(executeType)) { moduleLogger->InsertEntryPointModule(module); } // Return capability.[[Promise]]. return capability.GetTaggedValue(); } int SourceTextModule::EvaluateForConcurrent(JSThread *thread, const JSHandle &module, const JSHandle &method) { // 1. Let module be this Source Text Module Record. // 2. Assert: module.[[Status]] is "instantiated" or "evaluated". [[maybe_unused]] ModuleStatus status = module->GetStatus(); ASSERT((status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATED)); // 4. Let result be InnerModuleEvaluation(module, stack, 0) int result = SourceTextModule::ModuleEvaluation(thread, module, 0, method); // 5. If result is an abrupt completion, then if (thread->HasPendingException()) { return result; } else { job::MicroJobQueue::ExecutePendingJob(thread, thread->GetEcmaVM()->GetMicroJobQueue()); return SourceTextModule::UNDEFINED_INDEX; } } int SourceTextModule::InnerModuleEvaluationUnsafe(JSThread *thread, JSHandle &module, CVector> &stack, CVector> &errorStack, int index, const void *buffer, size_t size, const ExecuteTypes &executeType) { STACK_LIMIT_CHECK(thread, index); ModuleStatus status = module->GetStatus(); if (status >= ModuleStatus::EVALUATING_ASYNC) { if (status == ModuleStatus::ERRORED) { ModuleMessageHelper::PrintAndThrowError(thread, module); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); } return index; } if (status == ModuleStatus::EVALUATING) { return index; } ASSERT(status == ModuleStatus::INSTANTIATED); module->SetStatus(ModuleStatus::EVALUATING); module->SetDFSIndex(index); module->SetDFSAncestorIndex(index); module->SetPendingAsyncDependencies(0); index++; stack.emplace_back(module); ModuleLogger *moduleLogger = thread->GetModuleLogger(); if (!module->GetRequestedModules(thread).IsUndefined()) { JSHandle requestedModules(thread, module->GetRequestedModules(thread)); size_t requestedModulesLen = requestedModules->GetLength(); JSHandle requiredModule; for (size_t idx = 0; idx < requestedModulesLen; idx++) { // check if requiredModule is marked lazy if (module->IsLazyImportModule(idx)) { continue; } requiredModule = GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, idx); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); if (moduleLogger != nullptr) { moduleLogger->InsertParentModule(module, requiredModule); } ModuleTypes moduleType = requiredModule->GetTypes(); if (SourceTextModule::IsNativeModule(moduleType)) { EvaluateNativeModule(thread, requiredModule, moduleType); continue; } index = SourceTextModule::InnerModuleEvaluation( thread, requiredModule, stack, errorStack, index, buffer, size, executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); ModuleStatus requiredModuleStatus = requiredModule->GetStatus(); ASSERT(requiredModuleStatus >= ModuleStatus::EVALUATING); if (requiredModuleStatus == ModuleStatus::EVALUATING) { ASSERT(std::find(stack.begin(), stack.end(), requiredModule) != stack.end()); } if (std::find(stack.begin(), stack.end(), requiredModule) != stack.end()) { ASSERT(requiredModuleStatus == ModuleStatus::EVALUATING); } if (requiredModuleStatus == ModuleStatus::EVALUATING) { int dfsAncIdx = std::min(module->GetDFSAncestorIndex(), requiredModule->GetDFSAncestorIndex()); module->SetDFSAncestorIndex(dfsAncIdx); } else { requiredModule = JSHandle(thread, requiredModule->GetCycleRoot(thread)); requiredModuleStatus = requiredModule->GetStatus(); ASSERT(requiredModuleStatus >= ModuleStatus::EVALUATING_ASYNC); if (requiredModuleStatus == ModuleStatus::ERRORED) { errorStack.emplace_back(module); SetExceptionToModule(thread, module, requiredModule->GetException(thread)); ModuleMessageHelper::PrintAndThrowError(thread, module); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); return index; } } if (requiredModule->IsAsyncEvaluating()) { module->SetPendingAsyncDependencies(module->GetPendingAsyncDependencies() + 1); AddAsyncParentModule(thread, requiredModule, module); } } } int pendingAsyncDependencies = module->GetPendingAsyncDependencies(); bool hasTLA = module->GetHasTLA(); // 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is true, then if (pendingAsyncDependencies > 0 || hasTLA) { // a. Assert: module.[[AsyncEvaluation]] is false and was never previously set to true. ASSERT(module->GetAsyncEvaluatingOrdinal() == NOT_ASYNC_EVALUATED); // b. Set module.[[AsyncEvaluation]] to true. auto moduleManager = thread->GetModuleManager(); module->SetAsyncEvaluatingOrdinal(moduleManager->NextModuleAsyncEvaluatingOrdinal()); // d. If module.[[PendingAsyncDependencies]] = 0, perform ExecuteAsyncModule(module). if (pendingAsyncDependencies == 0) { SourceTextModule::ExecuteAsyncModule(thread, module, buffer, size, executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); } } else { // 13. Else, Perform ? module.ExecuteModule(). SourceTextModule::ModuleExecution(thread, module, buffer, size, executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); } // 14. Assert: module occurs exactly once in stack. // 15. Assert: module.[[DFSAncestorIndex]] ≤ module.[[DFSIndex]]. int dfsAncIdx = module->GetDFSAncestorIndex(); int dfsIdx = module->GetDFSIndex(); ASSERT(dfsAncIdx <= dfsIdx); // 16. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then if (dfsAncIdx == dfsIdx) { // a. Let done be false. bool done = false; // b. Repeat, while done is false, while (!done) { // i. Let requiredModule be the last element in stack. JSHandle requiredModule = stack.back(); // ii. Remove the last element of stack. stack.pop_back(); // iii. Assert: requiredModule is a Cyclic Module Record. // iv. If requiredModule.[[AsyncEvaluation]] is false, set requiredModule.[[Status]] to EVALUATED. // v. Otherwise, set requiredModule.[[Status]] to EVALUATING-ASYNC. if (!requiredModule->IsAsyncEvaluating()) { requiredModule->SetStatus(ModuleStatus::EVALUATED); } else { requiredModule->SetStatus(ModuleStatus::EVALUATING_ASYNC); } // vi. If requiredModule and module are the same Module Record, set done to true. if (JSTaggedValue::SameValue(thread, module.GetTaggedValue(), requiredModule.GetTaggedValue())) { done = true; } // vii. Set requiredModule.[[CycleRoot]] to module. if (!SourceTextModule::IsSharedModule(requiredModule)) { requiredModule->SetCycleRoot(thread, module); } } } return index; } bool SourceTextModule::IsEvaluatedModule(JSThread *thread, StateVisit &stateVisit, const JSHandle &module) { // Status maybe EVALUATED || ERRORED. return GetModuleEvaluatingType(thread, stateVisit, module) >= ModuleStatus::EVALUATED; } ModuleStatus SourceTextModule::GetModuleEvaluatingType(JSThread *thread, StateVisit &stateVisit, const JSHandle &module) { RuntimeLockHolder locker(thread, stateVisit.mutex); return module->GetStatus(); } int SourceTextModule::InnerModuleEvaluation(JSThread *thread, JSHandle &module, CVector> &stack, CVector> &errorStack, int index, const void *buffer, size_t size, const ExecuteTypes &executeType) { bool isShared = IsSharedModule(module); if (!isShared) { return SourceTextModule::InnerModuleEvaluationUnsafe( thread, module, stack, errorStack, index, buffer, size, executeType); } else { SharedModuleManager* sharedModuleManager = SharedModuleManager::GetInstance(); StateVisit &stateVisit = sharedModuleManager->FindModuleMutexWithLock(thread, module); ModuleStatus status = module->GetStatus(); if (status == ModuleStatus::EVALUATING && stateVisit.threadId == thread->GetThreadId()) { return index; } if (status == ModuleStatus::ERRORED) { ModuleMessageHelper::PrintAndThrowError(thread, module); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); return index; } RuntimeLockHolder locker(thread, stateVisit.mutex); module = sharedModuleManager-> TransferFromLocalToSharedModuleMapAndGetInsertedSModule(thread, module); if (module->GetStatus() == ModuleStatus::INSTANTIATED) { stateVisit.threadId = thread->GetThreadId(); int idx = SourceTextModule::InnerModuleEvaluationUnsafe( thread, module, stack, errorStack, index, buffer, size, executeType); return idx; } return index; } LOG_FULL(FATAL) << "This line is unreachable"; UNREACHABLE(); } void SourceTextModule::HandleConcurrentEvaluateResult(JSThread *thread, JSHandle &module, const CVector> &stack, const CVector> &errorStack) { ModuleStatus status; // 9. If result is an abrupt completion, then if (thread->HasPendingException()) { JSHandle exception(thread, thread->GetException()); HandleEvaluateException(thread, stack, exception); // b. Assert: module.[[Status]] is "evaluated" and module.[[EvaluationError]] is result. return; } if (!errorStack.empty()) { return HandleErrorStack(thread, errorStack); } // a. Assert: module.[[Status]] is either EVALUATING-ASYNC or EVALUATED. status = module->GetStatus(); ASSERT(status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED || status != ModuleStatus::ERRORED); // c. If module.[[AsyncEvaluation]] is false, then // i. Assert: module.[[Status]] is EVALUATED || ERRORED. if (!module->IsAsyncEvaluating()) { ASSERT(status >= ModuleStatus::EVALUATED); } // d. Assert: stack is empty. ASSERT(stack.empty()); } int SourceTextModule::ModuleEvaluation(JSThread *thread, const JSHandle &module, int index, const JSHandle &method) { if (!module->GetRequestedModules(thread).IsUndefined()) { JSHandle requestedModules(thread, module->GetRequestedModules(thread)); size_t requestedModulesLen = requestedModules->GetLength(); JSMutableHandle required(thread, thread->GlobalConstants()->GetUndefined()); auto coRequestedModules = GetConcurrentRequestedModules(thread, method); for (size_t idx = 0; idx < requestedModulesLen; idx++) { if (coRequestedModules.has_value() && coRequestedModules.value().count(idx) == 0) { // skip the unused module continue; } JSHandle requiredModule = GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, idx); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); ModuleTypes moduleType = requiredModule->GetTypes(); if (SourceTextModule::IsNativeModule(moduleType)) { EvaluateNativeModule(thread, requiredModule, moduleType); continue; } CVector> stack; CVector> errorStack; int result = SourceTextModule::InnerModuleEvaluation(thread, requiredModule, stack, errorStack, 0); index += result; HandleConcurrentEvaluateResult(thread, requiredModule, stack, errorStack); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); } } return index; } Expected SourceTextModule::ModuleExecution(JSThread *thread, const JSHandle &module, const void *buffer, size_t size, const ExecuteTypes &executeType) { CString moduleFilenameStr {}; if (thread->GetStageOfHotReload() == StageOfHotReload::LOAD_END_EXECUTE_PATCHMAIN) { moduleFilenameStr = thread->GetEcmaVM()->GetQuickFixManager()->GetBaseFileName(module); } else { moduleFilenameStr = module->GetEcmaModuleFilenameString(); } std::string entryPoint; CString moduleRecordName = module->GetEcmaModuleRecordNameString(); if (moduleRecordName.empty()) { entryPoint = JSPandaFile::ENTRY_FUNCTION_NAME; } else { entryPoint = moduleRecordName; } std::shared_ptr jsPandaFile; if (buffer != nullptr) { jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, moduleFilenameStr, entryPoint, buffer, size); } else { jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile( thread, moduleFilenameStr, entryPoint, false, executeType); } RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, Unexpected(false)); if (jsPandaFile == nullptr) { // LCOV_EXCL_BR_LINE LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << moduleFilenameStr; } return JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entryPoint, executeType); } void SourceTextModule::AddImportEntry(JSThread *thread, const JSHandle &module, const JSHandle &importEntry, size_t idx, uint32_t len) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue importEntries = module->GetImportEntries(thread); if (importEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(len); array->Set(thread, idx, importEntry.GetTaggedValue()); module->SetImportEntries(thread, array); } else { JSHandle entries(thread, importEntries); if (len > entries->GetLength()) { entries = TaggedArray::SetCapacity(thread, entries, len); entries->Set(thread, idx, importEntry.GetTaggedValue()); module->SetImportEntries(thread, entries); return; } entries->Set(thread, idx, importEntry.GetTaggedValue()); } } void SourceTextModule::AddLocalExportEntry(JSThread *thread, const JSHandle &module, const JSHandle &exportEntry, size_t idx, uint32_t len) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue localExportEntries = module->GetLocalExportEntries(thread); if (localExportEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(len); array->Set(thread, idx, exportEntry.GetTaggedValue()); module->SetLocalExportEntries(thread, array); } else { JSHandle entries(thread, localExportEntries); entries->Set(thread, idx, exportEntry.GetTaggedValue()); } } void SourceTextModule::AddIndirectExportEntry(JSThread *thread, const JSHandle &module, const JSHandle &exportEntry, size_t idx, uint32_t len) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue indirectExportEntries = module->GetIndirectExportEntries(thread); if (indirectExportEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(len); array->Set(thread, idx, exportEntry.GetTaggedValue()); module->SetIndirectExportEntries(thread, array); } else { JSHandle entries(thread, indirectExportEntries); entries->Set(thread, idx, exportEntry.GetTaggedValue()); } } void SourceTextModule::AddStarExportEntry(JSThread *thread, const JSHandle &module, const JSHandle &exportEntry, size_t idx, uint32_t len) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue starExportEntries = module->GetStarExportEntries(thread); if (starExportEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(len); array->Set(thread, idx, exportEntry.GetTaggedValue()); module->SetStarExportEntries(thread, array); } else { JSHandle entries(thread, starExportEntries); entries->Set(thread, idx, exportEntry.GetTaggedValue()); } } JSTaggedValue SourceTextModule::GetModuleValue(JSThread *thread, int32_t index, bool isThrow) { JSTaggedValue dictionary = GetNameDictionary(thread); if (dictionary.IsUndefined()) { // if module is Errored, throw origin jsError this->CheckAndThrowModuleError(thread); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); if (isThrow) { CString errorMsg = GetEcmaModuleRecordNameString(); errorMsg = errorMsg.empty() ? GetEcmaModuleFilenameString() : errorMsg; errorMsg.append(" environment is undefined"); THROW_REFERENCE_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception()); } return JSTaggedValue::Hole(); } TaggedArray *array = TaggedArray::Cast(dictionary.GetTaggedObject()); return array->Get(thread, index); } JSTaggedValue SourceTextModule::GetModuleValue(JSThread *thread, JSTaggedValue key, bool isThrow) { JSTaggedValue dictionary = GetNameDictionary(thread); if (dictionary.IsUndefined()) { // if module is Errored, throw origin jsError this->CheckAndThrowModuleError(thread); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); if (isThrow) { CString errorMsg = GetEcmaModuleRecordNameString(); errorMsg = errorMsg.empty() ? GetEcmaModuleFilenameString() : errorMsg; errorMsg.append(" environment is undefined"); THROW_REFERENCE_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception()); } return JSTaggedValue::Hole(); } NameDictionary *dict = NameDictionary::Cast(dictionary.GetTaggedObject()); int entry = dict->FindEntry(thread, key); if (entry != -1) { return dict->GetValue(thread, entry); } // when key is exportName, need to get localName JSTaggedValue exportEntriesTv = GetLocalExportEntries(thread); if (!exportEntriesTv.IsUndefined()) { JSTaggedValue resolution = FindByExport(thread, exportEntriesTv, key, dictionary); if (!resolution.IsHole()) { return resolution; } } return JSTaggedValue::Hole(); } JSTaggedValue SourceTextModule::GetValueFromExportObject(JSThread *thread, JSHandle &exportObject, int32_t index) { if (index == SourceTextModule::UNDEFINED_INDEX) { return exportObject.GetTaggedValue(); } JSTaggedValue value = JSTaggedValue::Hole(); JSObject *obj = JSObject::Cast(exportObject.GetTaggedValue()); TaggedArray *properties = TaggedArray::Cast(obj->GetProperties(thread).GetTaggedObject()); if (!properties->IsDictionaryMode()) { JSHClass *jsHclass = obj->GetJSHClass(); LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout(thread).GetTaggedObject()); PropertyAttributes attr = layoutInfo->GetAttr(thread, index); value = obj->GetProperty(thread, jsHclass, attr); } else { NameDictionary *dict = NameDictionary::Cast(properties); value = dict->GetValue(thread, index); } if (UNLIKELY(value.IsAccessor())) { return FastRuntimeStub::CallGetter(thread, JSTaggedValue(obj), JSTaggedValue(obj), value); } return value; } JSTaggedValue SourceTextModule::FindByExport(JSThread *thread, const JSTaggedValue &exportEntriesTv, const JSTaggedValue &key, const JSTaggedValue &dictionary) { DISALLOW_GARBAGE_COLLECTION; NameDictionary *dict = NameDictionary::Cast(dictionary.GetTaggedObject()); TaggedArray *exportEntries = TaggedArray::Cast(exportEntriesTv.GetTaggedObject()); size_t exportEntriesLen = exportEntries->GetLength(); for (size_t idx = 0; idx < exportEntriesLen; idx++) { LocalExportEntry *ee = LocalExportEntry::Cast(exportEntries->Get(thread, idx).GetTaggedObject()); if (!JSTaggedValue::SameValue(thread, ee->GetExportName(thread), key)) { continue; } JSTaggedValue localName = ee->GetLocalName(thread); int entry = dict->FindEntry(thread, localName); if (entry != -1) { return dict->GetValue(thread, entry); } } return JSTaggedValue::Hole(); } // static void SourceTextModule::StoreModuleValue(JSThread *thread, const JSHandle &module, int32_t index, const JSHandle &value) { if (UNLIKELY(IsSharedModule(module)) && !value->IsSharedType()) { CString msg = "Export non-shared object from shared-module, module name is :" + module->GetEcmaModuleRecordNameString(); THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } JSTaggedValue localExportEntries = module->GetLocalExportEntries(thread); ASSERT(localExportEntries.IsTaggedArray()); JSHandle data(thread, module->GetNameDictionary(thread)); if (data->IsUndefined()) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t size = TaggedArray::Cast(localExportEntries.GetTaggedObject())->GetLength(); ASSERT(index < static_cast(size)); if (SourceTextModule::IsSharedModule(module)) { data = JSHandle(factory->NewSTaggedArray(size, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE)); } else { data = JSHandle(factory->NewTaggedArray(size)); } module->SetNameDictionary(thread, data); } JSHandle arr(data); arr->Set(thread, index, value); } // static // discard instructions won't consider shared-module. void SourceTextModule::StoreModuleValue(JSThread *thread, const JSHandle &module, const JSHandle &key, const JSHandle &value) { if (UNLIKELY(IsSharedModule(module)) && !value->IsSharedType()) { CString msg = "Export non-shared object from shared-module, module name is :" + module->GetEcmaModuleRecordNameString(); THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } JSMutableHandle data(thread, module->GetNameDictionary(thread)); if (data->IsUndefined()) { data.Update(NameDictionary::Create(thread, DEFAULT_DICTIONART_CAPACITY)); } JSHandle dataDict = JSHandle::Cast(data); data.Update(NameDictionary::Put(thread, dataDict, key, value, PropertyAttributes::Default())); module->SetNameDictionary(thread, data); } void SourceTextModule::SetExportName(JSThread *thread, const JSHandle requestedModule, CVector &exportedNames, JSHandle &newExportStarSet) { // b. Let starNames be ? requestedModule.GetExportedNames(exportStarSet). CVector starNames = SourceTextModule::GetExportedNames(thread, requestedModule, newExportStarSet); // c. For each element n of starNames, do for (std::string &nn : starNames) { // i. If SameValue(n, "default") is false, then if (nn != "default" && std::find(exportedNames.begin(), exportedNames.end(), nn) == exportedNames.end()) { // 1. If n is not an element of exportedNames, then // a. Append n to exportedNames. exportedNames.emplace_back(nn); } } } JSHandle SourceTextModule::GetStarResolution(JSThread *thread, const JSHandle &exportName, const JSHandle importedModule, JSMutableHandle &starResolution, ResolvedMultiMap &resolvedMap) { // b. Let resolution be ? importedModule.ResolveExport(exportName, resolvedMap). auto moduleType = importedModule->GetTypes(); bool isNativeModule = IsNativeModule(moduleType); JSHandle resolution; if (UNLIKELY(isNativeModule || moduleType == ModuleTypes::CJS_MODULE)) { thread->GetEcmaVM()->GetJSOptions().SetDisableModuleSnapshot(true); resolution = isNativeModule ? SourceTextModule::ResolveNativeStarExport(thread, importedModule, exportName) : SourceTextModule::ResolveCjsStarExport(thread, importedModule, exportName); } else { resolution = SourceTextModule::ResolveExport(thread, importedModule, exportName, resolvedMap); } RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); // c. If resolution is "ambiguous", return "ambiguous". auto globalConstants = thread->GlobalConstants(); if (resolution->IsString()) { // if resolution is string, resolution must be "ambiguous" return globalConstants->GetHandledAmbiguousString(); } // d. If resolution is not null, then if (resolution->IsNull()) { return globalConstants->GetHandledNull(); } // i. Assert: resolution is a ResolvedBinding Record. ASSERT(resolution->IsResolvedBinding() || resolution->IsResolvedIndexBinding()); // ii. If starResolution is null, set starResolution to resolution. if (starResolution->IsNull()) { starResolution.Update(resolution.GetTaggedValue()); } else { // 1. Assert: There is more than one * import that includes the requested name. // 2. If resolution.[[Module]] and starResolution.[[Module]] are not the same Module Record or // SameValue( // resolution.[[BindingName]], starResolution.[[BindingName]]) is false, return "ambiguous". // Adapter new opcode if (resolution->IsResolvedBinding()) { JSHandle resolutionBd = JSHandle::Cast(resolution); JSHandle starResolutionBd = JSHandle::Cast(starResolution); if ((!JSTaggedValue::SameValue(thread, resolutionBd->GetModule(thread), starResolutionBd->GetModule(thread))) || (!JSTaggedValue::SameValue(thread, resolutionBd->GetBindingName(thread), starResolutionBd->GetBindingName(thread)))) { return globalConstants->GetHandledAmbiguousString(); } } else { JSHandle resolutionBd = JSHandle::Cast(resolution); JSHandle starResolutionBd = JSHandle::Cast(starResolution); if ((!JSTaggedValue::SameValue(thread, resolutionBd->GetModule(thread), starResolutionBd->GetModule(thread))) || resolutionBd->GetIndex() != starResolutionBd->GetIndex()) { return globalConstants->GetHandledAmbiguousString(); } } } return resolution; } template void SourceTextModule::AddExportName(JSThread *thread, const JSTaggedValue &exportEntry, CVector &exportedNames) { if (!exportEntry.IsUndefined()) { JSMutableHandle ee(thread, thread->GlobalConstants()->GetUndefined()); JSHandle exportEntries(thread, exportEntry); size_t exportEntriesLen = exportEntries->GetLength(); for (size_t idx = 0; idx < exportEntriesLen; idx++) { ee.Update(exportEntries->Get(thread, idx)); // a. Assert: module provides the direct binding for this export. // b. Append e.[[ExportName]] to exportedNames. std::string exportName = EcmaStringAccessor(ee->GetExportName(thread)).ToStdString(thread); exportedNames.emplace_back(exportName); } } } JSHandle SourceTextModule::ResolveElementOfObject(JSThread *thread, const JSHandle &hclass, const JSHandle &exportName, const JSHandle &module) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); int idx = JSHClass::FindPropertyEntry(thread, *hclass, exportName.GetTaggedValue()); if (idx != -1) { return JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, idx)); } return thread->GlobalConstants()->GetHandledUndefined(); } JSHandle SourceTextModule::ResolveLocalExport(JSThread *thread, const JSHandle &exportEntry, const JSHandle &exportName, const JSHandle &module) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSMutableHandle ee(thread, thread->GlobalConstants()->GetUndefined()); JSMutableHandle localName(thread, thread->GlobalConstants()->GetUndefined()); JSHandle localExportEntries(exportEntry); size_t localExportEntriesLen = localExportEntries->GetLength(); for (size_t idx = 0; idx < localExportEntriesLen; idx++) { ee.Update(localExportEntries->Get(thread, idx)); // a. If SameValue(exportName, e.[[ExportName]]) is true, then // if module is type of CommonJS or native, export first, check after execution. auto moduleType = module->GetTypes(); if (IsNativeModule(moduleType) || moduleType == ModuleTypes::CJS_MODULE) { return JSHandle::Cast(factory->NewResolvedBindingRecord(module, exportName)); } if ((JSTaggedValue::SameValueString(thread, ee->GetExportName(thread), exportName.GetTaggedValue()))) { // Adapter new module if (module->GetIsNewBcVersion()) { return JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, ee->GetLocalIndex())); } // i. Assert: module provides the direct binding for this export. // ii. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }. localName.Update(ee->GetLocalName(thread)); return JSHandle::Cast(factory->NewResolvedBindingRecord(module, localName)); } } return thread->GlobalConstants()->GetHandledUndefined(); } JSHandle SourceTextModule::ResolveIndirectExport(JSThread *thread, const JSHandle &exportEntry, const JSHandle &exportName, const JSHandle &module, ResolvedMultiMap &resolvedMap) { auto globalConstants = thread->GlobalConstants(); JSTaggedValue undefined = globalConstants->GetUndefined(); JSMutableHandle ee(thread, undefined); JSMutableHandle importName(thread, undefined); JSHandle requestedModules(thread, module->GetRequestedModules(thread)); JSHandle indirectExportEntries(exportEntry); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(thread, idx)); // a. If SameValue(exportName, e.[[ExportName]]) is true, then if (JSTaggedValue::SameValueString(thread, exportName.GetTaggedValue(), ee->GetExportName(thread))) { // i. Assert: module imports a specific binding for this export. // ii. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]). JSHandle requestedModule = GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, ee->GetModuleRequestIndex()); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); ASSERT(requestedModule.GetTaggedValue().IsSourceTextModule()); // iii. Return importedModule.ResolveExport(e.[[ImportName]], resolvedMap). importName.Update(ee->GetImportName(thread)); return SourceTextModule::ResolveExport(thread, requestedModule, importName, resolvedMap); } } return thread->GlobalConstants()->GetHandledUndefined(); } void SourceTextModule::CheckResolvedBinding(JSThread *thread, const JSHandle &module) { auto globalConstants = thread->GlobalConstants(); // 1. For each ExportEntry Record e in module.[[IndirectExportEntries]], do JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(thread); if (indirectExportEntriesTv.IsUndefined()) { return; } JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle exportName(thread, globalConstants->GetUndefined()); JSHandle indirectExportEntries(thread, indirectExportEntriesTv); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(thread, idx)); // a. Let resolution be ? module.ResolveExport(e.[[ExportName]], « »). exportName.Update(ee->GetExportName(thread)); ResolvedMultiMap resolvedMap; JSHandle resolution = SourceTextModule::ResolveExport(thread, module, exportName, resolvedMap); RETURN_IF_ABRUPT_COMPLETION(thread); // b. If resolution is null or "ambiguous", throw a SyntaxError exception. if (resolution->IsNull() || resolution->IsString()) { TaggedArray *requestArray = TaggedArray::Cast(module->GetModuleRequests(thread).GetTaggedObject()); JSTaggedValue moduleRequest = requestArray->Get(thread, ee->GetModuleRequestIndex()); CString requestMod = ModulePathHelper::ReformatPath(ConvertToString(thread, moduleRequest)); CString msg = "the requested module '" + requestMod + GetResolveErrorReason(resolution) + ConvertToString(thread, exportName.GetTaggedValue()); if (!module->GetEcmaModuleRecordNameString().empty()) { CString recordStr = ModulePathHelper::ReformatPath(module->GetEcmaModuleRecordNameString()); msg += "' which exported by '" + recordStr + "'"; } else { msg += "' which exported by '" + module->GetEcmaModuleFilenameString() + "'"; } THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } // c. Assert: resolution is a ResolvedBinding Record. ASSERT(resolution->IsResolvedBinding()); } } void SourceTextModule::CheckResolvedIndexBinding(JSThread *thread, const JSHandle &module) { auto globalConstants = thread->GlobalConstants(); // 1. For each ExportEntry Record e in module.[[IndirectExportEntries]], do JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(thread); if (indirectExportEntriesTv.IsUndefined()) { return; } JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle exportName(thread, globalConstants->GetUndefined()); JSHandle indirectExportEntries(thread, indirectExportEntriesTv); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(thread, idx)); // a. Let resolution be ? module.ResolveExport(e.[[ExportName]], « »). exportName.Update(ee->GetExportName(thread)); ResolvedMultiMap resolvedMap; JSHandle resolution = SourceTextModule::ResolveExport(thread, module, exportName, resolvedMap); RETURN_IF_ABRUPT_COMPLETION(thread); // b. If resolution is null or "ambiguous", throw a SyntaxError exception. if (resolution->IsNull() || resolution->IsString()) { TaggedArray *requestArray = TaggedArray::Cast(module->GetModuleRequests(thread).GetTaggedObject()); JSTaggedValue moduleRequest = requestArray->Get(thread, ee->GetModuleRequestIndex()); CString requestMod = ModulePathHelper::ReformatPath(ConvertToString(thread, moduleRequest)); CString msg = "the requested module '" + requestMod + GetResolveErrorReason(resolution) + ConvertToString(thread, exportName.GetTaggedValue()); if (!module->GetEcmaModuleRecordNameString().empty()) { CString record = ModulePathHelper::ReformatPath(module->GetEcmaModuleRecordNameString()); msg += "' which exported by '" + record + "'"; } else { msg += "' which exported by '" + module->GetEcmaModuleFilenameString() + "'"; } THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } } } CString SourceTextModule::GetModuleName(JSTaggedValue currentModule) { SourceTextModule *module = SourceTextModule::Cast(currentModule.GetTaggedObject()); CString recordName = module->GetEcmaModuleRecordNameString(); if (recordName.empty()) { recordName = module->GetEcmaModuleFilenameString(); } return recordName; } bool SourceTextModule::IsDynamicModule(LoadingTypes types) { return types == LoadingTypes::DYNAMITC_MODULE; } bool SourceTextModule::IsAsyncEvaluating() { return GetAsyncEvaluatingOrdinal() >= FIRST_ASYNC_EVALUATING_ORDINAL; } void SourceTextModule::AddAsyncParentModule(JSThread *thread, JSHandle &module, JSHandle &parent) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue asyncParentModules = module->GetAsyncParentModules(thread); if (asyncParentModules.IsUndefined()) { JSHandle array = factory->NewTaggedArray(1); array->Set(thread, 0, parent.GetTaggedValue()); module->SetAsyncParentModules(thread, array); } else { JSHandle array(thread, asyncParentModules); ASSERT(array->GetLength() > 0); array = TaggedArray::SetCapacity(thread, array, array->GetLength() + 1); array->Set(thread, array->GetLength() - 1, parent.GetTaggedValue()); module->SetAsyncParentModules(thread, array); } } void SourceTextModule::ExecuteAsyncModule(JSThread *thread, const JSHandle &module, const void *buffer, size_t size, const ExecuteTypes &executeType) { // 1. Assert: module.[[Status]] is either EVALUATING or EVALUATING-ASYNC. ASSERT(module->GetStatus() == ModuleStatus::EVALUATING || module->GetStatus() == ModuleStatus::EVALUATING_ASYNC); // 2. Assert: module.[[HasTLA]] is true. ASSERT(module->GetHasTLA()); CString moduleFilenameStr = module->GetEcmaModuleFilenameString(); std::string entryPoint; CString moduleRecordName = module->GetEcmaModuleRecordNameString(); if (moduleRecordName.empty()) { entryPoint = JSPandaFile::ENTRY_FUNCTION_NAME; } else { entryPoint = moduleRecordName; } std::shared_ptr jsPandaFile; if (buffer != nullptr) { jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, moduleFilenameStr, entryPoint, buffer, size); } else { jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile( thread, moduleFilenameStr, entryPoint, false, executeType); } RETURN_IF_ABRUPT_COMPLETION(thread); if (jsPandaFile == nullptr) { // LCOV_EXCL_BR_LINE LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << moduleFilenameStr; } Expected result = JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entryPoint, executeType); ASSERT(result.Value().IsJSPromise()); // 3. Let capability be ! NewPromiseCapability(%Promise%). // 4. Let fulfilledClosure be a new Abstract Closure with no parameters that captures module and performs // the following steps when called: // a. Perform AsyncModuleExecutionFulfilled(module). // b. Return undefined. // 5. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, "", « »). // 6. Let rejectedClosure be a new Abstract Closure with parameters (error) that captures module and performs // the following steps when called: // a. Perform AsyncModuleExecutionRejected(module, error). // b. Return undefined. // 7. Let onRejected be CreateBuiltinFunction(rejectedClosure, 0, "", « »). // 8. Perform PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected). JSHandle promise(thread, result.Value()); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle onFulfilled = factory->CreateJSAsyncModuleFulfilledFunction(); onFulfilled->SetModule(thread, module); JSHandle onRejected = factory->CreateJSAsyncModuleRejectedFunction(); onRejected->SetModule(thread, module); JSHandle tcap = JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); RETURN_IF_ABRUPT_COMPLETION(thread); builtins::BuiltinsPromise::PerformPromiseThen( thread, promise, JSHandle::Cast(onFulfilled), JSHandle::Cast(onRejected), tcap); } void SourceTextModule::GatherAvailableAncestors(JSThread *thread, const JSHandle &module, AsyncParentCompletionSet &execList) { auto globalConstants = thread->GlobalConstants(); JSTaggedValue asyncParentModulesValue = module->GetAsyncParentModules(thread); if (asyncParentModulesValue.IsUndefined()) { return; } JSMutableHandle cycleRoot(thread, globalConstants->GetUndefined()); JSHandle asyncParentModules(thread, asyncParentModulesValue); size_t asyncParentModulesLen = asyncParentModules->GetLength(); // 1. For each Cyclic Module Record m of module.[[AsyncParentModules]], do for (size_t idx = 0; idx < asyncParentModulesLen; idx++) { JSHandle parentModule(thread, asyncParentModules->Get(thread, idx)); // a. If execList does not contain m and m.[[CycleRoot]].[[EvaluationError]] is EMPTY, then cycleRoot.Update(parentModule->GetCycleRoot(thread)); if (execList.find(parentModule) == execList.end() && cycleRoot->GetStatus() != ModuleStatus::ERRORED) { // i. Assert: m.[[Status]] is EVALUATING-ASYNC. ASSERT(parentModule->GetStatus() == ModuleStatus::EVALUATING_ASYNC); // ii. Assert: m.[[EvaluationError]] is EMPTY. // iii. Assert: m.[[AsyncEvaluation]] is true. ASSERT(parentModule->IsAsyncEvaluating()); // iv. Assert: m.[[PendingAsyncDependencies]] > 0. ASSERT(parentModule->GetPendingAsyncDependencies() > 0); // v. Set m.[[PendingAsyncDependencies]] to m.[[PendingAsyncDependencies]] - 1. parentModule->SetPendingAsyncDependencies(parentModule->GetPendingAsyncDependencies() - 1); // vi. If m.[[PendingAsyncDependencies]] = 0, then // 1. Append m to execList. // 2. If m.[[HasTLA]] is false, perform GatherAvailableAncestors(m, execList). if (parentModule->GetPendingAsyncDependencies() == 0) { execList.insert(parentModule); if (!parentModule->GetHasTLA()) { GatherAvailableAncestors(thread, parentModule, execList); } } } } } void SourceTextModule::AsyncModuleExecutionFulfilled(JSThread *thread, const JSHandle &module) { // 1. If module.[[Status]] is EVALUATED, then // a. Assert: module.[[EvaluationError]] is not EMPTY. // b. Return UNUSED. if (module->GetStatus() == ModuleStatus::ERRORED) { ASSERT(!module->GetException(thread).IsHole()); return; } // 2. Assert: module.[[Status]] is EVALUATING-ASYNC. ASSERT(module->GetStatus() == ModuleStatus::EVALUATING_ASYNC); // 3. Assert: module.[[AsyncEvaluation]] is true. ASSERT(module->IsAsyncEvaluating()); // 4. Assert: module.[[EvaluationError]] is EMPTY. // 5. Set module.[[AsyncEvaluation]] to false. module->SetAsyncEvaluatingOrdinal(ASYNC_EVALUATE_DID_FINISH); // 6. Set module.[[Status]] to EVALUATED. module->SetStatus(ModuleStatus::EVALUATED); // 7. If module.[[TopLevelCapability]] is not EMPTY, then // a. Assert: module.[[CycleRoot]] is module. // b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »). auto globalConstants = thread->GlobalConstants(); JSTaggedValue topLevelCapabilityValue = module->GetTopLevelCapability(thread); if (!topLevelCapabilityValue.IsUndefined()) { ASSERT(JSTaggedValue::SameValue(thread, module->GetCycleRoot(thread), module.GetTaggedValue())); JSHandle topLevelCapability(thread, JSPromise::Cast(topLevelCapabilityValue.GetTaggedObject())); JSHandle undefinedValue = globalConstants->GetHandledUndefined(); JSPromise::FulfillPromise(thread, topLevelCapability, undefinedValue); RETURN_IF_ABRUPT_COMPLETION(thread); } // 8. Let execList be a new empty List. AsyncParentCompletionSet execList; // 9. Perform GatherAvailableAncestors(module, execList). // 10. Let sortedExecList be a List whose elements are the elements of execList, // in the order in which they had their [[AsyncEvaluation]] fields set to true in InnerModuleEvaluation. GatherAvailableAncestors(thread, module, execList); // 11. Assert: All elements of sortedExecList have their [[AsyncEvaluation]] field set to true, // [[PendingAsyncDependencies]] field set to 0, and [[EvaluationError]] field set to EMPTY. // 12. For each Cyclic Module Record m of sortedExecList, do for (JSHandle m : execList) { // a. If m.[[Status]] is EVALUATED, then // i. Assert: m.[[EvaluationError]] is not EMPTY. if (!m->IsAsyncEvaluating()) { ASSERT(module->GetStatus() == ModuleStatus::ERRORED); // b. Else if m.[[HasTLA]] is true, then // i. Perform ExecuteAsyncModule(m). } else if (m->GetHasTLA()) { ExecuteAsyncModule(thread, m); // c. Else, } else { // i. Let result be m.ExecuteModule(). Expected result = SourceTextModule::ModuleExecution(thread, m); // ii. If result is an abrupt completion, then // 1. Perform AsyncModuleExecutionRejected(m, result.[[Value]]). if (thread->HasPendingException() || !result || result.Value().IsException()) { AsyncModuleExecutionRejected(thread, m, JSTaggedValue::Exception()); // iii. Else, } else { // 1. Set m.[[Status]] to EVALUATED. m->SetStatus(ModuleStatus::EVALUATED); // 2. If m.[[TopLevelCapability]] is not EMPTY, then // a. Assert: m.[[CycleRoot]] is m. // b. Perform ! Call(m.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »). JSTaggedValue capabilityValue = m->GetTopLevelCapability(thread); if (!capabilityValue.IsUndefined()) { ASSERT(JSTaggedValue::SameValue(thread, m->GetCycleRoot(thread), m.GetTaggedValue())); JSHandle topLevelCapability(thread, JSPromise::Cast(topLevelCapabilityValue.GetTaggedObject())); JSHandle undefinedValue = globalConstants->GetHandledUndefined(); JSPromise::FulfillPromise(thread, topLevelCapability, undefinedValue); RETURN_IF_ABRUPT_COMPLETION(thread); } } } } } void SourceTextModule::AsyncModuleExecutionRejected(JSThread *thread, const JSHandle &module, JSTaggedValue error) { // 1. If module.[[Status]] is EVALUATED, then // a. Assert: module.[[EvaluationError]] is not EMPTY. // b. Return UNUSED. if (module->GetStatus() == ModuleStatus::ERRORED) { ASSERT(!module->GetException(thread).IsHole()); return; } // 2. Assert: module.[[Status]] is EVALUATING-ASYNC. ASSERT(module->GetStatus() == ModuleStatus::EVALUATING_ASYNC); // 3. Assert: module.[[AsyncEvaluation]] is true. ASSERT(module->IsAsyncEvaluating()); // 4. Assert: module.[[EvaluationError]] is EMPTY. ASSERT(module->GetException(thread).IsHole()); // 5. Set module.[[EvaluationError]] to ThrowCompletion(error). module->SetStatus(ModuleStatus::ERRORED); // 6. Set module.[[Status]] to EVALUATED. module->SetException(thread, error); // 7. For each Cyclic Module Record m of module.[[AsyncParentModules]], do // a. Perform AsyncModuleExecutionRejected(m, error). auto globalConstants = thread->GlobalConstants(); JSTaggedValue asyncParentModulesValue = module->GetAsyncParentModules(thread); if (!asyncParentModulesValue.IsUndefined()) { JSMutableHandle parentModule(thread, globalConstants->GetUndefined()); JSHandle asyncParentModules(thread, asyncParentModulesValue); size_t asyncParentModulesLen = asyncParentModules->GetLength(); for (size_t idx = 0; idx < asyncParentModulesLen; idx++) { parentModule.Update(asyncParentModules->Get(thread, idx)); AsyncModuleExecutionRejected(thread, parentModule, error); } } // 8. If module.[[TopLevelCapability]] is not EMPTY, then // a. Assert: module.[[CycleRoot]] is module. // b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]], undefined, « error »). JSTaggedValue topLevelCapabilityValue = module->GetTopLevelCapability(thread); if (!topLevelCapabilityValue.IsUndefined()) { JSHandle exceptionHandle(thread, error); // if caught exceptionHandle type is JSError if (exceptionHandle->IsJSError()) { thread->HandleUncaughtException(error); } ASSERT(JSTaggedValue::SameValue(thread, module->GetCycleRoot(thread), module.GetTaggedValue())); JSHandle topLevelCapability(thread, JSPromise::Cast(topLevelCapabilityValue.GetTaggedObject())); JSPromise::RejectPromise(thread, topLevelCapability, exceptionHandle); RETURN_IF_ABRUPT_COMPLETION(thread); } } JSTaggedValue SourceTextModule::AsyncModuleFulfilledFunc(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle fulfilledFunc = JSHandle::Cast(base::BuiltinsBase::GetConstructor(argv)); JSHandle module(thread, fulfilledFunc->GetModule(thread)); AsyncModuleExecutionFulfilled(thread, module); return JSTaggedValue::Undefined(); } JSTaggedValue SourceTextModule::AsyncModuleRejectedFunc(EcmaRuntimeCallInfo *argv) { // 1. Let F be the active function object. ASSERT(argv); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle rejectedFunc = JSHandle::Cast(base::BuiltinsBase::GetConstructor(argv)); JSHandle module(thread, rejectedFunc->GetModule(thread)); [[maybe_unused]] JSHandle value = base::BuiltinsBase::GetCallArg(argv, 0); AsyncModuleExecutionRejected(thread, module, value.GetTaggedValue()); return JSTaggedValue::Undefined(); } void SourceTextModule::CheckCircularImportTool(JSThread *thread, const CString &circularModuleRecordName, CList &referenceList, bool printOtherCircular) { referenceList.push_back(circularModuleRecordName); JSMutableHandle moduleRecord(thread, thread->GlobalConstants()->GetUndefined()); auto moduleManager = thread->GetModuleManager(); if (moduleManager->IsLocalModuleLoaded(circularModuleRecordName)) { moduleRecord.Update(moduleManager->HostGetImportedModule(circularModuleRecordName)); } else { moduleRecord.Update(ModuleResolver::HostResolveImportedModule(thread, circularModuleRecordName)); RETURN_IF_ABRUPT_COMPLETION(thread); } CString requiredModuleName; SourceTextModule::SearchCircularImport( thread, circularModuleRecordName, moduleRecord, referenceList, requiredModuleName, printOtherCircular); } void SourceTextModule::SearchCircularImport(JSThread *thread, const CString &circularModuleRecordName, const JSHandle &module, CList &referenceList, CString &requiredModuleName, bool printOtherCircular) { if (module->GetModuleRequests(thread).IsUndefined()) { return; } auto globalConstants = thread->GlobalConstants(); JSHandle moduleRequests(thread, module->GetModuleRequests(thread)); size_t moduleRequestsLen = moduleRequests->GetLength(); JSMutableHandle required(thread, globalConstants->GetUndefined()); JSMutableHandle requiredModule(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < moduleRequestsLen; idx++) { required.Update(moduleRequests->Get(thread, idx)); requiredModule.Update(JSHandle::Cast( ModuleResolver::HostResolveImportedModule(thread, module, required))); RETURN_IF_ABRUPT_COMPLETION(thread); requiredModuleName = requiredModule->GetEcmaModuleRecordNameString(); referenceList.push_back(requiredModuleName); if (requiredModuleName == circularModuleRecordName) { PrintCircular(referenceList, Level::ERROR); } else if (printOtherCircular && IsCircular(referenceList, requiredModuleName)) { PrintCircular(referenceList, Level::WARN); } else { SourceTextModule::SearchCircularImport(thread, circularModuleRecordName, requiredModule, referenceList, requiredModuleName, printOtherCircular); } referenceList.pop_back(); } } bool SourceTextModule::IsCircular(const CList &referenceList, const CString &requiredModuleName) { for (auto iter = referenceList.begin(), end = --referenceList.end(); iter != end; ++iter) { if (requiredModuleName == *iter) { return true; } } return false; } void SourceTextModule::PrintCircular(const CList &referenceList, Level level) { LOG_ECMA(INFO) << "checkCircularImport begin ----------------------------------------"; if (level == Level::ERROR) { for (auto iter : referenceList) { LOG_ECMA(ERROR) << "checkCircularImport record: " << iter; } } else { for (auto iter : referenceList) { LOG_ECMA(WARN) << "checkCircularImport record: " << iter; } } LOG_ECMA(INFO) << "checkCircularImport end ------------------------------------------"; } // This function for module which execution doesn't through SourceTextModule::Evaluate void SourceTextModule::RecordEvaluatedOrError(JSThread *thread, JSHandle module) { if (thread->HasPendingException()) { module->SetStatus(ModuleStatus::ERRORED); auto &options = const_cast(thread->GetEcmaVM())->GetJSOptions(); if (options.EnableModuleException()) { LOG_FULL(INFO) << "Error module: " << module->GetEcmaModuleRecordNameString(); } return SetExceptionToModule(thread, module, thread->GetException()); } module->SetStatus(ModuleStatus::EVALUATED); } void SourceTextModule::SetExceptionToModule(JSThread *thread, JSHandle module, JSTaggedValue exception) { if (!IsSharedModule(module)) { module->SetException(thread, exception); return; } JSHandle exceptionInfo(thread, exception); JSHandle ecmaErrMsg(thread, JSTaggedValue::Undefined()); bool hasPendingException = thread->HasPendingException(); if (hasPendingException) { thread->ClearException(); } // process error message for share module if (exceptionInfo->IsJSError()) { CString msg = base::ErrorHelper::GetJSErrorInfo(thread, exceptionInfo, base::ErrorHelper::JSErrorProps::MESSAGE); RETURN_IF_ABRUPT_COMPLETION(thread); CString stack = base::ErrorHelper::GetJSErrorInfo(thread, exceptionInfo, base::ErrorHelper::JSErrorProps::STACK); RETURN_IF_ABRUPT_COMPLETION(thread); CString errMsg = "Error store in module " + module->GetEcmaModuleRecordNameString() + ":\n" + msg + "\n" + stack; ecmaErrMsg = JSHandle::Cast(thread->GetEcmaVM()->GetFactory()->NewFromUtf8(errMsg)); } else { ecmaErrMsg = JSHandle::Cast(JSTaggedValue::ToString(thread, exceptionInfo)); RETURN_IF_ABRUPT_COMPLETION(thread); } if (hasPendingException) { thread->SetException(exceptionInfo.GetTaggedValue()); } module->SetException(thread, ecmaErrMsg.GetTaggedValue()); return; } // Add for dynamic import entry or other import ldvar bool SourceTextModule::CheckAndThrowModuleError(JSThread *thread) { if (GetStatus() == ModuleStatus::ERRORED) { LOG_FULL(ERROR) << "Error found in module:" << GetEcmaModuleRecordNameString(); JSHandle exceptionInfo(thread, GetException(thread)); if (exceptionInfo->IsJSError()) { base::ErrorHelper::PrintJSErrorInfo(thread, exceptionInfo); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, exceptionInfo.GetTaggedValue(), false); } JSHandle message = JSTaggedValue::ToString(thread, exceptionInfo); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString str = ConvertToString(thread, *message); LOG_NO_TAG(ERROR) << str; THROW_NEW_ERROR_AND_RETURN_VALUE(thread, message.GetTaggedValue(), false); } return true; } void SourceTextModule::HandleEvaluateException(JSThread *thread, const CVector> &stack, JSHandle exception) { // a. For each module m in stack, do auto &options = const_cast(thread->GetEcmaVM())->GetJSOptions(); if (options.EnableModuleException()) { JSTaggedValue::DesensitizedDump(thread, exception); } for (auto &mm : stack) { // i. Assert: m.[[Status]] is "evaluating". ASSERT(mm->GetStatus() == ModuleStatus::EVALUATING); // ii. Set m.[[Status]] to "evaluated". mm->SetStatus(ModuleStatus::ERRORED); if (options.EnableModuleException()) { LOG_FULL(INFO) << "Error module: " << mm->GetEcmaModuleRecordNameString(); } SetExceptionToModule(thread, mm, exception.GetTaggedValue()); } } void SourceTextModule::HandleErrorStack(JSThread *thread, const CVector> &errorStack) { auto &options = const_cast(thread->GetEcmaVM())->GetJSOptions(); for (auto &mm : errorStack) { ASSERT(mm->GetStatus() == ModuleStatus::EVALUATING || mm->GetStatus() == ModuleStatus::EVALUATED); // ii. Set m.[[Status]] to "error". mm->SetStatus(ModuleStatus::ERRORED); if (options.EnableModuleException()) { LOG_FULL(INFO) << "Error module: " << mm->GetEcmaModuleRecordNameString(); } } } JSHandle SourceTextModule::GetModuleFromCacheOrResolveNewOne(JSThread *thread, const JSHandle module, const JSHandle requestedModules, uint32_t idx) { JSHandle request(thread, requestedModules->Get(thread, idx)); JSHandle requireModule; if (!request->IsHole()) { if (request->IsSourceTextModule()) { // current is normal module: directly get require SourceTextModule. return JSHandle::Cast(request); } // current is shared module: resolve or find request module by request string. ModuleManager *moduleManager = thread->GetModuleManager(); CString requestStr = ModulePathHelper::Utf8ConvertToString(thread, request.GetTaggedValue()); // may return undefined requireModule = moduleManager->GetImportedModule(requestStr); } if (requireModule.GetTaggedValue().IsSourceTextModule()) { return requireModule; } /* * case A import B, A is sharedModule, B is normalModule. * Thread 1 Instantiate: 1. resolve A/B, add A to resolvedSharedModules_ , add B to resolvedModules_ * 2. mark A/B as INSTANTIATED. * Thread 1 Evaluate: Doesn't evaluate A immediately. * Thread 2 Instantiate: find A in resolvedSharedModules_, it's stauts is INSTANTIATED, return; * Thread 2 Evaluate: 1. evaluate B first, then evaluate A. * 2. find B through [GetRequestedModuleFromCache], crash. * Because [GetRequestedModuleFromCache] is a fastpath for resolvedModule, but B is not resolved in thread 2. * In this case, we need to add resolve new module process. */ JSHandle moduleRequests(thread, module->GetModuleRequests(thread)); JSHandle required(thread, moduleRequests->Get(thread, idx)); JSHandle requiredModule = ModuleResolver::HostResolveImportedModule(thread, module, required); RETURN_HANDLE_IF_ABRUPT_COMPLETION(SourceTextModule, thread); SetRequestedModules(thread, requestedModules, idx, requiredModule, SourceTextModule::IsSharedModule(module)); return JSHandle::Cast(requiredModule); } JSHandle SourceTextModule::GetRequestedModuleMayThrowError(JSThread *thread, const JSHandle module, uint32_t idx, const JSHandle requestedModules, JSHandle exception) { JSHandle request(thread, requestedModules->Get(thread, idx)); if (UNLIKELY(request->IsHole())) { // requestModule is hole, means module resolve already have exception. // throw original error. LOG_ECMA(ERROR) << "GetRequestedModuleMayThrowError request module is hole"; thread->SetException(exception.GetTaggedValue()); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); } return JSHandle::Cast(GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, idx)); } /* * case A import B * if B is sharedModule, every thread will instantiate one if sharedModule B have not put on sharedModuleMap. * In this case, current thread's B may not be the final shared module in sharedModuleMap, * so we shoule use recordName instead of SourceTextModule, * and use [GetImportedModule] to get the final sharedModule B. * * normal -> normal: SourceTextModule * normal -> shared: recordName * shared -> normal: recordName * shared -> shared: recordName */ void SourceTextModule::SetRequestedModules(JSThread *thread, JSHandle requestedModules, uint32_t idx, JSHandle requiredModule, bool isShared) { if (!isShared && !IsSharedModule(JSHandle::Cast(requiredModule))) { requestedModules->Set(thread, idx, requiredModule.GetTaggedValue()); } else { CString recordName = GetModuleName(requiredModule.GetTaggedValue()); JSHandle requireModuleName = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(recordName); requestedModules->Set(thread, idx, requireModuleName.GetTaggedValue()); } } void SourceTextModule::StoreAndResetMutableFields(JSThread* thread, JSHandle module, MutableFields& fields) { JSTaggedValue undefinedValue = thread->GlobalConstants()->GetUndefined(); fields.TopLevelCapability = module->GetTopLevelCapability(thread); fields.NameDictionary = module->GetNameDictionary(thread); fields.CycleRoot = module->GetCycleRoot(thread); fields.AsyncParentModules = module->GetAsyncParentModules(thread); fields.SendableEnv = module->GetSendableEnv(thread); fields.Exception = module->GetException(thread); fields.Namespace = module->GetNamespace(thread); module->SetTopLevelCapability(thread, undefinedValue); module->SetNameDictionary(thread, undefinedValue); module->SetCycleRoot(thread, undefinedValue); module->SetAsyncParentModules(thread, undefinedValue); module->SetSendableEnv(thread, undefinedValue); module->SetException(thread, undefinedValue); module->SetNamespace(thread, undefinedValue); } void SourceTextModule::RestoreMutableFields(JSThread* thread, JSHandle module, MutableFields& fields) { module->SetTopLevelCapability(thread, fields.TopLevelCapability); module->SetNameDictionary(thread, fields.NameDictionary); module->SetCycleRoot(thread, fields.CycleRoot); module->SetAsyncParentModules(thread, fields.AsyncParentModules); module->SetSendableEnv(thread, fields.SendableEnv); module->SetException(thread, fields.Exception); module->SetNamespace(thread, fields.Namespace); } JSHandle SourceTextModule::CreateBindingByIndexBinding(JSThread* thread, JSHandle binding, bool isShared) { JSHandle resolvedModule(thread, binding->GetModule(thread)); JSHandle bindingName = GetBindingNameByIndex(thread, resolvedModule, binding->GetIndex()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); if (isShared) { return JSHandle::Cast(factory->NewSResolvedBindingRecord(resolvedModule, bindingName)); } return JSHandle::Cast(factory->NewResolvedBindingRecord(resolvedModule, bindingName)); } JSHandle SourceTextModule::FindFuncInModuleForHook(JSThread* thread, const std::string &recordName, const std::string &namespaceName, const std::string &className, const std::string &funcName) { DisallowGarbageCollection no_gc; JSHandle functionNotFound(thread, thread->GlobalConstants()->GetUndefined()); auto *moduleManager = thread->GetModuleManager(); CString referencing(recordName.c_str(), recordName.length()); if (!moduleManager->IsLocalModuleLoaded(referencing)) { return functionNotFound; } auto module = moduleManager->HostGetImportedModule(referencing); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); std::string keyStr = funcName; if (!namespaceName.empty()) { keyStr = namespaceName; } else if (!className.empty()) { keyStr = className; } JSTaggedValue result; if (module->GetIsNewBcVersion()) { int index = ecmascript::ModuleManager::GetExportObjectIndex(thread->GetEcmaVM(), module, keyStr.c_str()); result = module->GetModuleValue(thread, index, false); } else { JSHandle keyHandle = factory->NewFromASCII(keyStr.c_str()); result = module->GetModuleValue(thread, keyHandle.GetTaggedValue(), false); } JSHandle exportEntity(thread, result); if (exportEntity->IsUndefined() || exportEntity->IsHole()) { return functionNotFound; } return JSObject::FindFuncInObjectForHook(thread, exportEntity, className, funcName); } } // namespace panda::ecmascript