* 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_data_extractor.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<std::string> SourceTextModule::GetExportedNames(JSThread *thread, const JSHandle<SourceTextModule> &module,
const JSHandle<TaggedArray> &exportStarSet)
{
CVector<std::string> exportedNames;
if (exportStarSet->GetIdx(thread, module.GetTaggedValue()) != TaggedArray::MAX_ARRAY_INDEX) {
return exportedNames;
}
size_t len = exportStarSet->GetLength();
JSHandle<TaggedArray> newExportStarSet = TaggedArray::SetCapacity(thread, exportStarSet, len + 1);
newExportStarSet->Set(thread, len, module.GetTaggedValue());
JSTaggedValue entryValue = module->GetLocalExportEntries(thread);
AddExportName<LocalExportEntry>(thread, entryValue, exportedNames);
entryValue = module->GetIndirectExportEntries(thread);
AddExportName<IndirectExportEntry>(thread, entryValue, exportedNames);
entryValue = module->GetStarExportEntries(thread);
auto globalConstants = thread->GlobalConstants();
if (!entryValue.IsUndefined()) {
JSMutableHandle<StarExportEntry> ee(thread, globalConstants->GetUndefined());
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
JSHandle<TaggedArray> starExportEntries(thread, entryValue);
size_t starExportEntriesLen = starExportEntries->GetLength();
for (size_t idx = 0; idx < starExportEntriesLen; idx++) {
ee.Update(starExportEntries->Get(thread, idx));
JSHandle<SourceTextModule> 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<SourceTextModule> &module,
const JSHandle<JSTaggedValue> &exportName,
ResolvedMultiMap &resolvedMap)
{
if (resolvedMap.empty()) {
return false;
}
auto range = resolvedMap.equal_range(reinterpret_cast<CString *>(GetModuleName(module)));
for (auto iter = range.first; iter != range.second; ++iter) {
auto name = iter->second;
if (JSTaggedValue::StringCompare(thread, EcmaString::Cast(name.GetTaggedValue().GetTaggedObject()),
EcmaString::Cast(exportName.GetTaggedValue().GetTaggedObject()))) {
return true;
}
}
return false;
}
JSHandle<JSTaggedValue> SourceTextModule::GetBindingNameByIndex(JSThread *thread,
const JSHandle<SourceTextModule> module,
const int index)
{
auto globalConstants = thread->GlobalConstants();
if (index == SourceTextModule::UNDEFINED_INDEX) {
return globalConstants->GetHandledDefaultString();
}
JSHandle<JSTaggedValue> 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> jsHClass(thread, exportObject->GetJSHClass());
LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHClass->GetLayout(thread).GetTaggedObject());
if (layoutInfo->NumberOfElements() > index) {
JSHandle<JSTaggedValue> key(thread, layoutInfo->GetKey(thread, index));
return key;
}
} else {
NameDictionary *dict = NameDictionary::Cast(properties);
if (dict->Size() > index) {
JSHandle<JSTaggedValue> key(thread, dict->GetKey(thread, index));
return key;
}
}
}
return globalConstants->GetHandledUndefined();
}
JSHandle<JSTaggedValue> SourceTextModule::ResolveExportObject(JSThread *thread,
const JSHandle<SourceTextModule> &module,
const JSHandle<JSTaggedValue> &exports,
const JSHandle<JSTaggedValue> &exportName)
{
auto globalConstants = thread->GlobalConstants();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> defaultString = globalConstants->GetHandledDefaultString();
if (JSTaggedValue::SameValueString(thread, exportName, defaultString)) {
return JSHandle<JSTaggedValue>::Cast(factory->NewResolvedIndexBindingRecord(module, -1));
}
if (exports->IsNativeModuleFailureInfo()) {
return JSHandle<JSTaggedValue>::Cast(factory->NewResolvedIndexBindingRecord(module, -1));
}
if (exports->IsJSObject()) {
JSHandle<JSTaggedValue> resolution(thread, JSTaggedValue::Undefined());
JSObject *exportObject = JSObject::Cast(exports.GetTaggedValue().GetTaggedObject());
TaggedArray *properties = TaggedArray::Cast(exportObject->GetProperties(thread).GetTaggedObject());
if (!properties->IsDictionaryMode()) {
JSHandle<JSHClass> jsHclass(thread, exportObject->GetJSHClass());
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<JSTaggedValue>::Cast(factory->NewResolvedIndexBindingRecord(module, entry));
}
}
if (!resolution->IsUndefined()) {
return resolution;
}
}
return globalConstants->GetHandledNull();
}
JSHandle<JSTaggedValue> SourceTextModule::ResolveNativeStarExport(JSThread *thread,
const JSHandle<SourceTextModule> &nativeModule,
const JSHandle<JSTaggedValue> &exportName)
{
ModuleTypes moduleType = nativeModule->GetTypes();
if (!SourceTextModule::EvaluateNativeModule(thread, nativeModule, moduleType)) {
return thread->GlobalConstants()->GetHandledNull();
}
JSHandle<JSTaggedValue> nativeExports(thread, nativeModule->GetModuleValue(thread, 0, false));
return SourceTextModule::ResolveExportObject(thread, nativeModule, nativeExports, exportName);
}
JSHandle<JSTaggedValue> SourceTextModule::ResolveCjsStarExport(JSThread *thread,
const JSHandle<SourceTextModule> &cjsModule,
const JSHandle<JSTaggedValue> &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<JSTaggedValue> cjsModuleName(thread->GetEcmaVM()->GetFactory()->NewFromUtf8(moduleName));
JSHandle<JSTaggedValue> cjsExports = CjsModule::SearchFromModuleCache(thread, cjsModuleName);
return SourceTextModule::ResolveExportObject(thread, cjsModule, cjsExports, exportName);
}
JSHandle<JSTaggedValue> SourceTextModule::ResolveExport(JSThread *thread,
const JSHandle<SourceTextModule> &module,
const JSHandle<JSTaggedValue> &exportName,
ResolvedMultiMap &resolvedMap)
{
auto globalConstants = thread->GlobalConstants();
if (CheckCircularImport(thread, module, exportName, resolvedMap)) {
return globalConstants->GetHandledNull();
}
resolvedMap.emplace(reinterpret_cast<CString *>(GetModuleName(module)), exportName);
JSHandle<JSTaggedValue> localExportEntriesTv(thread, module->GetLocalExportEntries(thread));
if (!localExportEntriesTv->IsUndefined()) {
JSHandle<JSTaggedValue> resolution = ResolveLocalExport(thread, localExportEntriesTv, exportName, module);
if (!resolution->IsUndefined()) {
return resolution;
}
}
JSHandle<JSTaggedValue> indirectExportEntriesTv(thread, module->GetIndirectExportEntries(thread));
if (!indirectExportEntriesTv->IsUndefined()) {
JSHandle<JSTaggedValue> resolution = ResolveIndirectExport(thread, indirectExportEntriesTv,
exportName, module, resolvedMap);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
if (!resolution->IsUndefined()) {
return resolution;
}
}
JSHandle<JSTaggedValue> defaultString = globalConstants->GetHandledDefaultString();
if (JSTaggedValue::SameValueString(thread, exportName, defaultString)) {
return globalConstants->GetHandledNull();
}
JSMutableHandle<JSTaggedValue> starResolution(thread, globalConstants->GetNull());
JSTaggedValue starExportEntriesTv = module->GetStarExportEntries(thread);
if (starExportEntriesTv.IsUndefined()) {
return starResolution;
}
JSMutableHandle<StarExportEntry> ee(thread, globalConstants->GetUndefined());
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
JSHandle<TaggedArray> starExportEntries(thread, starExportEntriesTv);
size_t starExportEntriesLen = starExportEntries->GetLength();
for (size_t idx = 0; idx < starExportEntriesLen; idx++) {
ee.Update(starExportEntries->Get(thread, idx));
JSHandle<SourceTextModule> requestedModule =
GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, ee->GetModuleRequestIndex());
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
ASSERT(requestedModule.GetTaggedValue().IsSourceTextModule());
JSHandle<JSTaggedValue> result = GetStarResolution(thread, exportName, requestedModule,
starResolution, resolvedMap);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
if (result->IsString() || result->IsException()) {
return result;
}
}
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<JSTaggedValue> 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> 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<JSTaggedValue>(vm->GetJSThread(), func);
}
EcmaRuntimeCallInfo* SourceTextModule::MakeNormalizedAppArgs(const EcmaVM *vm, JSHandle<JSTaggedValue> func,
const CString &soPath, const CString &moduleName)
{
JSThread *thread = vm->GetJSThread();
const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
ObjectFactory *factory = vm->GetFactory();
JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
CString soName = ModulePathHelper::GetNormalizedPathFromOhmUrl(soPath);
CString path = base::ConcatToCString(ModulePathHelper::GetBundleNameFromNormalized(vm, soPath),
PathHelper::SLASH_TAG, moduleName);
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, undefined, 3);
RETURN_VALUE_IF_ABRUPT(thread, nullptr);
JSHandle<EcmaString> arg0 = factory->NewFromUtf8(soName.c_str());
JSHandle<EcmaString> arg2 = factory->NewFromUtf8(path.c_str());
info->SetCallArg(arg0.GetTaggedValue(),
globalConstants->GetTrue(),
arg2.GetTaggedValue());
return info;
}
EcmaRuntimeCallInfo* SourceTextModule::MakeAppArgs(const EcmaVM *vm, JSHandle<JSTaggedValue> 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<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
size_t pos = soPath.find_last_of(PathHelper::SLASH_TAG);
if (pos == CString::npos) {
LOG_FULL(FATAL) << "Invalid native module " << soPath;
UNREACHABLE();
}
CString soName = soPath.substr(pos + 1);
CString path = soPath.substr(0, pos);
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, undefined, 3);
RETURN_VALUE_IF_ABRUPT(thread, nullptr);
JSHandle<EcmaString> arg0 = factory->NewFromUtf8(soName.c_str());
JSHandle<EcmaString> arg2 = factory->NewFromUtf8(path.c_str());
info->SetCallArg(arg0.GetTaggedValue(),
globalConstants->GetTrue(),
arg2.GetTaggedValue());
return info;
}
EcmaRuntimeCallInfo* SourceTextModule::MakeInternalArgs(const EcmaVM *vm, JSHandle<JSTaggedValue> func,
const CString &soPath, const CString &moduleRequestName)
{
JSThread* thread = vm->GetJSThread();
const GlobalEnvConstants* globalConstants = thread->GlobalConstants();
ObjectFactory* factory = vm->GetFactory();
JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
CString moduleDir = PathHelper::GetInternalModulePrefix(moduleRequestName);
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, undefined, 4);
RETURN_VALUE_IF_ABRUPT(thread, nullptr);
JSHandle<EcmaString> arg0 = factory->NewFromUtf8(soPath.c_str());
JSHandle<EcmaString> arg3 = factory->NewFromUtf8(moduleDir.c_str());
info->SetCallArg(arg0.GetTaggedValue(),
globalConstants->GetFalse(),
globalConstants->GetEmptyString(),
arg3.GetTaggedValue());
return info;
}
JSHandle<JSTaggedValue> 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<JSTaggedValue> resultValue(thread, result);
EcmaVM::ClearKeptObjects(thread);
vm->GetJsDebuggerManager()->NotifyReturnNative();
return resultValue;
}
JSHandle<JSTaggedValue> SourceTextModule::LoadNativeModuleImpl(EcmaVM *vm, JSThread *thread,
const JSHandle<SourceTextModule> &requiredModule, ModuleTypes moduleType)
{
CString moduleRequestName = requiredModule->GetEcmaModuleRecordNameString();
ModuleTraceScope moduleTraceScope = ModuleTraceScope::Open(
thread, "SourceTextModule::LoadNativeModule:", moduleRequestName);
auto undefined = vm->GetJSThread()->GlobalConstants()->GetHandledUndefined();
ModuleLoggerTimeScope moduleLoggerTimeScope(thread, moduleRequestName);
CString soName = PathHelper::GetStrippedModuleName(moduleRequestName);
CString fileName = requiredModule->GetEcmaModuleFilenameString();
CString moduleName = ModulePathHelper::GetModuleNameWithBaseFile(fileName);
LOG_FULL(DEBUG) << "Request module is " << moduleRequestName;
JSHandle<JSTaggedValue> func = GetRequireNativeModuleFunc(vm, moduleType);
if (!func->IsCallable()) {
LOG_FULL(WARN) << "Not found require func";
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);
JSHandle<JSTaggedValue> exportObject = LoadNativeModuleCallFunc(vm, info);
RETURN_VALUE_IF_ABRUPT(thread, undefined);
return exportObject;
}
JSHandle<JSTaggedValue> SourceTextModule::LoadNativeModuleMayThrowError(JSThread *thread,
const JSHandle<SourceTextModule> &requiredModule, ModuleTypes moduleType)
{
EcmaVM *vm = thread->GetEcmaVM();
auto exportObject = LoadNativeModuleImpl(vm, thread, requiredModule, moduleType);
if (exportObject->IsNativeModuleFailureInfo() || exportObject->IsUndefined()) {
SourceTextModule::StoreModuleValue(thread, requiredModule, 0, exportObject);
CString errorMsg = "load native module failed.";
LOG_FULL(DEBUG) << 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<SourceTextModule> &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())) {
SourceTextModule::StoreModuleValue(thread, requiredModule, 0, exportObject);
LOG_ECMA(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_ECMA(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<SourceTextModule> nativeModule,
ModuleTypes moduleType)
{
if (nativeModule->GetStatus() == ModuleStatus::EVALUATED) {
return true;
}
if (!SourceTextModule::LoadNativeModule(thread, nativeModule, moduleType)) {
LOG_FULL(DEBUG) << "LoadNativeModule " << nativeModule->GetEcmaModuleRecordNameString() << " failed";
return false;
}
nativeModule->SetStatus(ModuleStatus::EVALUATED);
return true;
}
int SourceTextModule::HandleInstantiateException([[maybe_unused]] JSHandle<SourceTextModule> &module,
const CVector<JSHandle<SourceTextModule>> &stack, int result)
{
for (auto mm : stack) {
ASSERT(mm->GetStatus() == ModuleStatus::INSTANTIATING);
mm->SetStatus(ModuleStatus::UNINSTANTIATED);
mm->SetDFSIndex(SourceTextModule::UNDEFINED_INDEX);
mm->SetDFSAncestorIndex(SourceTextModule::UNDEFINED_INDEX);
}
ASSERT(module->GetStatus() == ModuleStatus::UNINSTANTIATED);
return result;
}
int SourceTextModule::Instantiate(JSThread *thread, const JSHandle<JSTaggedValue> &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<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleHdl);
ModuleStatus status = module->GetStatus();
ASSERT(status == ModuleStatus::UNINSTANTIATED || status == ModuleStatus::INSTANTIATED ||
status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED ||
status == ModuleStatus::ERRORED);
SourceTextModule::PreModuleInstantiation(thread, module, executeType);
JSHandle<JSTaggedValue> exception;
if (thread->HasPendingException()) {
exception = JSHandle<JSTaggedValue>(thread, thread->GetException());
thread->ClearExceptionAndExtraErrorMessage();
}
CVector<JSHandle<SourceTextModule>> stack;
int result = FinishModuleInstantiation(thread, module, stack, 0, exception);
if (thread->HasPendingException()) {
return HandleInstantiateException(module, stack, result);
}
status = module->GetStatus();
ASSERT(status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATING_ASYNC ||
status == ModuleStatus::EVALUATED || status == ModuleStatus::ERRORED);
ASSERT(stack.empty());
return SourceTextModule::UNDEFINED_INDEX;
}
std::optional<std::set<uint32_t>> SourceTextModule::GetConcurrentRequestedModules(JSThread *thread,
const JSHandle<Method> &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<SourceTextModule> &module,
CVector<JSHandle<SourceTextModule>> &stack)
{
int dfsAncIdx = module->GetDFSAncestorIndex();
int dfsIdx = module->GetDFSIndex();
ASSERT(dfsAncIdx <= dfsIdx);
if (dfsAncIdx == dfsIdx) {
bool done = false;
while (!done) {
JSHandle<SourceTextModule> requiredModule = stack.back();
stack.pop_back();
requiredModule->SetStatus(ModuleStatus::INSTANTIATED);
if (JSTaggedValue::SameValue(thread, module.GetTaggedValue(), requiredModule.GetTaggedValue())) {
done = true;
}
}
}
}
bool SourceTextModule::PreModuleInstantiation(JSThread *thread,
JSHandle<SourceTextModule> module, const ExecuteTypes &executeType)
{
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 (status == ModuleStatus::EVALUATING) {
LOG_ECMA(DEBUG) << "circular dependency detected, current module: " << *SourceTextModule::GetModuleName(module);
if (isShared) {
LOG_FULL(INFO) << "circular dependency occurred of shared-module";
return true;
}
}
module->SetStatus(ModuleStatus::PREINSTANTIATING);
JSHandle<TaggedArray> moduleRequests(thread, module->GetModuleRequests(thread));
if (!moduleRequests.GetTaggedValue().IsUndefined()) {
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
size_t moduleRequestsLen = moduleRequests->GetLength();
JSMutableHandle<JSTaggedValue> required(thread, thread->GlobalConstants()->GetUndefined());
for (size_t idx = 0; idx < moduleRequestsLen; idx++) {
required.Update(moduleRequests->Get(thread, idx));
JSHandle<JSTaggedValue> requiredModule =
ModuleResolver::HostResolveImportedModule(thread, module, required, executeType);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
SetRequestedModules(thread, requestedModules, idx, requiredModule, isShared);
PreModuleInstantiation(thread, JSHandle<SourceTextModule>::Cast(requiredModule), executeType);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
}
}
return true;
}
int SourceTextModule::FinishModuleInstantiation(JSThread *thread, JSHandle<SourceTextModule> module,
CVector<JSHandle<SourceTextModule>> &stack, int index, JSHandle<JSTaggedValue> exception)
{
ModuleStatus status = module->GetStatus();
if (status >= ModuleStatus::INSTANTIATING) {
return index;
}
ASSERT(status == ModuleStatus::PREINSTANTIATING);
module->SetStatus(ModuleStatus::INSTANTIATING);
module->SetDFSIndex(index);
module->SetDFSAncestorIndex(index);
index++;
stack.emplace_back(module);
if (!module->GetRequestedModules(thread).IsUndefined()) {
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
size_t requestedModulesLen = requestedModules->GetLength();
for (size_t idx = 0; idx < requestedModulesLen; idx++) {
JSHandle<JSTaggedValue> moduleHdl =
GetRequestedModuleMayThrowError(thread, module, idx, requestedModules, exception);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
JSHandle<SourceTextModule> requiredModule = JSHandle<SourceTextModule>::Cast(moduleHdl);
index = FinishModuleInstantiation(thread, requiredModule, stack, index, exception);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
ModuleStatus requiredModuleStatus = requiredModule->GetStatus();
ASSERT(requiredModuleStatus >= ModuleStatus::INSTANTIATING &&
requiredModuleStatus != ModuleStatus::EVALUATING);
if (requiredModuleStatus == ModuleStatus::INSTANTIATING) {
ASSERT(std::find(stack.begin(), stack.end(), requiredModule) != stack.end());
int dfsAncIdx = std::min(module->GetDFSAncestorIndex(), requiredModule->GetDFSAncestorIndex());
module->SetDFSAncestorIndex(dfsAncIdx);
}
}
}
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<SourceTextModule> &module)
{
thread->CheckSafepointIfSuspended();
CheckResolvedBinding(thread, module);
if (module->GetImportEntries(thread).IsUndefined()) {
return;
}
ASSERT(!SourceTextModule::IsSharedModule(module));
JSHandle<TaggedArray> importEntries(thread, module->GetImportEntries(thread));
size_t importEntriesLen = importEntries->GetLength();
JSHandle<NameDictionary> map(NameDictionary::Create(thread,
NameDictionary::ComputeHashTableSize(importEntriesLen)));
module->SetEnvironment(thread, map);
JSMutableHandle<JSTaggedValue> envRec(thread, module->GetEnvironment(thread));
ASSERT(!envRec->IsUndefined());
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
auto globalConstants = thread->GlobalConstants();
JSMutableHandle<ImportEntry> in(thread, globalConstants->GetUndefined());
JSMutableHandle<JSTaggedValue> importName(thread, globalConstants->GetUndefined());
JSMutableHandle<JSTaggedValue> 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));
JSHandle<SourceTextModule> importedModule = JSHandle<SourceTextModule>::Cast(
GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, in->GetModuleRequestIndex()));
RETURN_IF_ABRUPT_COMPLETION(thread);
ASSERT(importedModule.GetTaggedValue().IsSourceTextModule());
JSHandle<JSTaggedValue> starString = globalConstants->GetHandledStarString();
if (JSTaggedValue::SameValueString(thread, importName, starString)) {
JSHandle<JSTaggedValue> moduleNamespace = SourceTextModule::GetModuleNamespace(thread, importedModule);
JSHandle<NameDictionary> mapHandle = JSHandle<NameDictionary>::Cast(envRec);
JSHandle<NameDictionary> newMap = NameDictionary::Put(thread, mapHandle, localName, moduleNamespace,
PropertyAttributes::Default());
envRec.Update(newMap);
} else {
ResolvedMultiMap resolvedMap;
JSHandle<JSTaggedValue> resolution =
SourceTextModule::ResolveExport(thread, importedModule, importName, resolvedMap);
RETURN_IF_ABRUPT_COMPLETION(thread);
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());
}
JSHandle<NameDictionary> mapHandle = JSHandle<NameDictionary>::Cast(envRec);
JSHandle<NameDictionary> newMap = NameDictionary::Put(thread, mapHandle, localName, resolution,
PropertyAttributes::Default());
envRec.Update(newMap);
}
}
module->SetEnvironment(thread, envRec);
}
void SourceTextModule::ModuleDeclarationArrayEnvironmentSetup(JSThread *thread,
const JSHandle<SourceTextModule> &module)
{
thread->CheckSafepointIfSuspended();
ModuleTraceScope moduleTraceScope = ModuleTraceScope::Open(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();
JSHandle<TaggedArray> importEntries(thread, module->GetImportEntries(thread));
size_t importEntriesLen = importEntries->GetLength();
JSHandle<TaggedArray> arr = factory->NewTaggedArray(importEntriesLen);
JSHandle<TaggedArray> envRec = arr;
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
auto globalConstants = thread->GlobalConstants();
JSMutableHandle<ImportEntry> in(thread, globalConstants->GetUndefined());
JSMutableHandle<JSTaggedValue> importName(thread, globalConstants->GetUndefined());
for (size_t idx = 0; idx < importEntriesLen; idx++) {
in.Update(importEntries->Get(thread, idx));
importName.Update(in->GetImportName(thread));
JSHandle<SourceTextModule> importedModule =
GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, in->GetModuleRequestIndex());
RETURN_IF_ABRUPT_COMPLETION(thread);
ASSERT(importedModule.GetTaggedValue().IsSourceTextModule());
JSHandle<JSTaggedValue> starString = globalConstants->GetHandledStarString();
if (JSTaggedValue::SameValueString(thread, importName, starString)) {
envRec = JSSharedModule::CloneEnvForSModule(thread, module, envRec);
module->SetEnvironment(thread, envRec);
return;
}
ResolvedMultiMap resolvedMap;
JSHandle<JSTaggedValue> resolution =
SourceTextModule::ResolveExport(thread, importedModule, importName, resolvedMap);
RETURN_IF_ABRUPT_COMPLETION(thread);
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());
}
envRec->Set(thread, idx, resolution);
}
envRec = JSSharedModule::CloneEnvForSModule(thread, module, envRec);
module->SetEnvironment(thread, envRec);
}
JSHandle<JSTaggedValue> SourceTextModule::GetModuleNamespace(JSThread *thread,
const JSHandle<SourceTextModule> &module)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
ASSERT(module->GetStatus() != ModuleStatus::UNINSTANTIATED);
JSMutableHandle<JSTaggedValue> moduleNamespace(thread, module->GetNamespace(thread).GetWeakRawValue());
if (moduleNamespace->IsUndefined()) {
JSHandle<TaggedArray> exportStarSet = factory->EmptyArray();
CVector<std::string> exportedNames = SourceTextModule::GetExportedNames(thread, module, exportStarSet);
JSHandle<TaggedArray> unambiguousNames = factory->NewTaggedArray(exportedNames.size());
size_t idx = 0;
for (std::string &name : exportedNames) {
ResolvedMultiMap resolvedMap;
JSHandle<JSTaggedValue> nameHandle = JSHandle<JSTaggedValue>::Cast(factory->NewFromStdString(name));
JSHandle<JSTaggedValue> resolution =
SourceTextModule::ResolveExport(thread, module, nameHandle, resolvedMap);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
if (resolution->IsModuleBinding()) {
unambiguousNames->Set(thread, idx, nameHandle);
idx++;
}
}
JSHandle<TaggedArray> fixUnambiguousNames = TaggedArray::SetCapacity(thread, unambiguousNames, idx);
JSHandle<JSTaggedValue> moduleTagged = JSHandle<JSTaggedValue>::Cast(module);
JSHandle<ModuleNamespace> np =
ModuleNamespace::ModuleNamespaceCreate(thread, moduleTagged, fixUnambiguousNames);
moduleNamespace.Update(np.GetTaggedValue());
}
return moduleNamespace;
}
void SourceTextModule::HandleEvaluateResult(JSThread *thread, JSHandle<SourceTextModule> &module,
JSHandle<JSPromise> &capability, const CVector<JSHandle<SourceTextModule>> &stack,
const CVector<JSHandle<SourceTextModule>> &errorStack)
{
ModuleStatus status;
if (thread->HasPendingException()) {
JSHandle<JSTaggedValue> exception(thread, thread->GetException());
HandleEvaluateException(thread, stack, exception);
JSPromise::RejectPromise(thread, capability, exception);
RETURN_IF_ABRUPT_COMPLETION(thread);
return;
}
if (!errorStack.empty()) {
return HandleErrorStack(thread, errorStack);
}
status = module->GetStatus();
ASSERT(status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED);
ASSERT(status != ModuleStatus::ERRORED);
if (!module->IsAsyncEvaluating()) {
ASSERT(status >= ModuleStatus::EVALUATED);
}
ASSERT(stack.empty());
}
JSTaggedValue SourceTextModule::Evaluate(JSThread *thread, const JSHandle<SourceTextModule> &moduleHdl,
const void *buffer, size_t size, const ExecuteTypes &executeType)
{
CString traceInfo = "SourceTextModule::Evaluate, entryPoint:" + GetModuleName(moduleHdl.GetTaggedValue()) +
",isLazy:" + CString(executeType == ExecuteTypes::LAZY ? "true" : "false");
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, traceInfo.c_str(), "");
JSMutableHandle<SourceTextModule> module(thread, moduleHdl);
ModuleStatus status = module->GetStatus();
ASSERT((status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATING_ASYNC ||
status == ModuleStatus::EVALUATED));
if (status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED) {
module.Update(module->GetCycleRoot(thread));
}
CVector<JSHandle<SourceTextModule>> stack;
CVector<JSHandle<SourceTextModule>> errorStack;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSPromise> capability = factory->NewJSPromise();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!SourceTextModule::IsSharedModule(module)) {
module->SetTopLevelCapability(thread, capability);
}
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.GetTaggedValue();
}
int SourceTextModule::EvaluateForConcurrent(JSThread *thread, const JSHandle<SourceTextModule> &module,
const JSHandle<Method> &method)
{
[[maybe_unused]] ModuleStatus status = module->GetStatus();
ASSERT((status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATED));
int result = SourceTextModule::ModuleEvaluation(thread, module, 0, method);
if (thread->HasPendingException()) {
return result;
} else {
job::MicroJobQueue::ExecutePendingJob(thread, thread->GetEcmaVM()->GetMicroJobQueue());
return SourceTextModule::UNDEFINED_INDEX;
}
}
int SourceTextModule::InnerModuleEvaluationUnsafe(JSThread *thread, JSHandle<SourceTextModule> &module,
CVector<JSHandle<SourceTextModule>> &stack, CVector<JSHandle<SourceTextModule>> &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);
ModuleImportStackScope scope(thread, module);
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<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
size_t requestedModulesLen = requestedModules->GetLength();
JSHandle<SourceTextModule> requiredModule;
for (size_t idx = 0; idx < requestedModulesLen; idx++) {
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<SourceTextModule>(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();
if (pendingAsyncDependencies > 0 || hasTLA) {
ASSERT(module->GetAsyncEvaluatingOrdinal() == NOT_ASYNC_EVALUATED);
auto moduleManager = thread->GetModuleManager();
module->SetAsyncEvaluatingOrdinal(moduleManager->NextModuleAsyncEvaluatingOrdinal());
if (pendingAsyncDependencies == 0) {
SourceTextModule::ExecuteAsyncModule(thread, module, buffer, size, executeType);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
}
} else {
SourceTextModule::ModuleExecution(thread, module, buffer, size, executeType);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
}
int dfsAncIdx = module->GetDFSAncestorIndex();
int dfsIdx = module->GetDFSIndex();
ASSERT(dfsAncIdx <= dfsIdx);
if (dfsAncIdx == dfsIdx) {
bool done = false;
while (!done) {
JSHandle<SourceTextModule> requiredModule = stack.back();
stack.pop_back();
if (!requiredModule->IsAsyncEvaluating()) {
requiredModule->SetStatus(ModuleStatus::EVALUATED);
#if ENABLE_MEMORY_OPTIMIZATION
ClearImportEntriesIfNeeded(thread, requiredModule);
#endif
} else {
requiredModule->SetStatus(ModuleStatus::EVALUATING_ASYNC);
}
if (JSTaggedValue::SameValue(thread, module.GetTaggedValue(), requiredModule.GetTaggedValue())) {
done = true;
}
if (!SourceTextModule::IsSharedModule(requiredModule)) {
requiredModule->SetCycleRoot(thread, module);
}
}
}
return index;
}
void SourceTextModule::ClearImportEntriesIfNeeded(JSThread *thread, JSHandle<SourceTextModule> &module)
{
EcmaVM *vm = thread->GetEcmaVM();
if ((!vm->GetJsDebuggerManager()->IsDebugApp()) && thread->GetModuleLogger() == nullptr) {
JSTaggedValue undefinedValue = thread->GlobalConstants()->GetUndefined();
module->SetImportEntries(thread, undefinedValue);
}
}
bool SourceTextModule::IsEvaluatedModule(JSThread *thread, StateVisit &stateVisit,
const JSHandle<SourceTextModule> &module)
{
return GetModuleEvaluatingType(thread, stateVisit, module) >= ModuleStatus::EVALUATED;
}
ModuleStatus SourceTextModule::GetModuleEvaluatingType(JSThread *thread, StateVisit &stateVisit,
const JSHandle<SourceTextModule> &module)
{
RuntimeLockHolder locker(thread, stateVisit.mutex);
return module->GetStatus();
}
int SourceTextModule::InnerModuleEvaluation(JSThread *thread, JSHandle<SourceTextModule> &module,
CVector<JSHandle<SourceTextModule>> &stack,
CVector<JSHandle<SourceTextModule>> &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<SourceTextModule> &module,
const CVector<JSHandle<SourceTextModule>> &stack, const CVector<JSHandle<SourceTextModule>> &errorStack)
{
ModuleStatus status;
if (thread->HasPendingException()) {
JSHandle<JSTaggedValue> exception(thread, thread->GetException());
HandleEvaluateException(thread, stack, exception);
return;
}
if (!errorStack.empty()) {
return HandleErrorStack(thread, errorStack);
}
status = module->GetStatus();
ASSERT(status == ModuleStatus::EVALUATING_ASYNC ||
status == ModuleStatus::EVALUATED ||
status != ModuleStatus::ERRORED);
if (!module->IsAsyncEvaluating()) {
ASSERT(status >= ModuleStatus::EVALUATED);
}
ASSERT(stack.empty());
}
int SourceTextModule::ModuleEvaluation(JSThread *thread, const JSHandle<SourceTextModule> &module,
int index, const JSHandle<Method> &method)
{
if (!module->GetRequestedModules(thread).IsUndefined()) {
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
size_t requestedModulesLen = requestedModules->GetLength();
JSMutableHandle<JSTaggedValue> 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) {
continue;
}
JSHandle<SourceTextModule> 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<JSHandle<SourceTextModule>> stack;
CVector<JSHandle<SourceTextModule>> 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<JSTaggedValue, bool> SourceTextModule::ModuleExecution(JSThread *thread,
const JSHandle<SourceTextModule> &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();
}
CString entryPoint;
CString moduleRecordName = module->GetEcmaModuleRecordNameString();
if (moduleRecordName.empty()) {
entryPoint = JSPandaFile::ENTRY_FUNCTION_NAME;
} else {
entryPoint = moduleRecordName;
}
std::shared_ptr<JSPandaFile> 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) {
LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << moduleFilenameStr;
}
if (module->GetTypes() == ModuleTypes::JSON_MODULE) {
Expected<JSTaggedValue, bool> result;
EvaluateJsonModule(thread, module, jsPandaFile.get(), CString(entryPoint));
return result;
}
return JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entryPoint, executeType);
}
void SourceTextModule::AddImportEntry(JSThread *thread, const JSHandle<SourceTextModule> &module,
const JSHandle<ImportEntry> &importEntry, size_t idx, uint32_t len)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue importEntries = module->GetImportEntries(thread);
if (importEntries.IsUndefined()) {
JSHandle<TaggedArray> array = factory->NewTaggedArray(len);
array->Set(thread, idx, importEntry.GetTaggedValue());
module->SetImportEntries(thread, array);
} else {
JSHandle<TaggedArray> 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<SourceTextModule> &module,
const JSHandle<LocalExportEntry> &exportEntry, size_t idx, uint32_t len)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue localExportEntries = module->GetLocalExportEntries(thread);
if (localExportEntries.IsUndefined()) {
JSHandle<TaggedArray> array = factory->NewTaggedArray(len);
array->Set(thread, idx, exportEntry.GetTaggedValue());
module->SetLocalExportEntries(thread, array);
} else {
JSHandle<TaggedArray> entries(thread, localExportEntries);
entries->Set(thread, idx, exportEntry.GetTaggedValue());
}
}
void SourceTextModule::AddIndirectExportEntry(JSThread *thread, const JSHandle<SourceTextModule> &module,
const JSHandle<IndirectExportEntry> &exportEntry,
size_t idx, uint32_t len)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue indirectExportEntries = module->GetIndirectExportEntries(thread);
if (indirectExportEntries.IsUndefined()) {
JSHandle<TaggedArray> array = factory->NewTaggedArray(len);
array->Set(thread, idx, exportEntry.GetTaggedValue());
module->SetIndirectExportEntries(thread, array);
} else {
JSHandle<TaggedArray> entries(thread, indirectExportEntries);
entries->Set(thread, idx, exportEntry.GetTaggedValue());
}
}
void SourceTextModule::AddStarExportEntry(JSThread *thread, const JSHandle<SourceTextModule> &module,
const JSHandle<StarExportEntry> &exportEntry, size_t idx, uint32_t len)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue starExportEntries = module->GetStarExportEntries(thread);
if (starExportEntries.IsUndefined()) {
JSHandle<TaggedArray> array = factory->NewTaggedArray(len);
array->Set(thread, idx, exportEntry.GetTaggedValue());
module->SetStarExportEntries(thread, array);
} else {
JSHandle<TaggedArray> 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()) {
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()) {
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);
}
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<JSTaggedValue> &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();
}
void SourceTextModule::StoreModuleValue(JSThread *thread, const JSHandle<SourceTextModule> &module, int32_t index,
const JSHandle<JSTaggedValue> &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<JSTaggedValue> 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<int32_t>(size));
if (SourceTextModule::IsSharedModule(module)) {
data = JSHandle<JSTaggedValue>(factory->NewSTaggedArray(size,
JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE));
} else {
data = JSHandle<JSTaggedValue>(factory->NewTaggedArray(size));
}
module->SetNameDictionary(thread, data);
}
JSHandle<TaggedArray> arr(data);
arr->Set(thread, index, value);
}
void SourceTextModule::StoreModuleValue(JSThread *thread, const JSHandle<SourceTextModule> &module,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &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<JSTaggedValue> data(thread, module->GetNameDictionary(thread));
if (data->IsUndefined()) {
data.Update(NameDictionary::Create(thread, DEFAULT_DICTIONART_CAPACITY));
}
JSHandle<NameDictionary> dataDict = JSHandle<NameDictionary>::Cast(data);
data.Update(NameDictionary::Put(thread, dataDict, key, value, PropertyAttributes::Default()));
module->SetNameDictionary(thread, data);
}
void SourceTextModule::SetExportName(JSThread *thread, const JSHandle<SourceTextModule> requestedModule,
CVector<std::string> &exportedNames, JSHandle<TaggedArray> &newExportStarSet)
{
CVector<std::string> starNames =
SourceTextModule::GetExportedNames(thread, requestedModule, newExportStarSet);
for (std::string &nn : starNames) {
if (nn != "default" && std::find(exportedNames.begin(), exportedNames.end(), nn) == exportedNames.end()) {
exportedNames.emplace_back(nn);
}
}
}
JSHandle<JSTaggedValue> SourceTextModule::GetStarResolution(JSThread *thread,
const JSHandle<JSTaggedValue> &exportName,
const JSHandle<SourceTextModule> importedModule,
JSMutableHandle<JSTaggedValue> &starResolution,
ResolvedMultiMap &resolvedMap)
{
auto moduleType = importedModule->GetTypes();
bool isNativeModule = IsNativeModule(moduleType);
JSHandle<JSTaggedValue> resolution;
if (UNLIKELY(isNativeModule || moduleType == ModuleTypes::CJS_MODULE)) {
if (!thread->GetEcmaVM()->GetJSOptions().DisableModuleSnapshot() ||
thread->GetEcmaVM()->GetJSOptions().EnableModuleLog()) {
thread->GetEcmaVM()->GetJSOptions().SetDisableModuleSnapshot(true);
LOG_ECMA(INFO) << "ModuleSnapshot is disabled: export * from "
<< GetModuleName(importedModule.GetTaggedValue());
}
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);
auto globalConstants = thread->GlobalConstants();
if (resolution->IsString()) {
return globalConstants->GetHandledAmbiguousString();
}
if (resolution->IsNull()) {
return globalConstants->GetHandledNull();
}
ASSERT(resolution->IsResolvedBinding() || resolution->IsResolvedIndexBinding());
if (starResolution->IsNull()) {
starResolution.Update(resolution.GetTaggedValue());
} else {
if (resolution->IsResolvedBinding()) {
JSHandle<ResolvedBinding> resolutionBd = JSHandle<ResolvedBinding>::Cast(resolution);
JSHandle<ResolvedBinding> starResolutionBd = JSHandle<ResolvedBinding>::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<ResolvedIndexBinding> resolutionBd = JSHandle<ResolvedIndexBinding>::Cast(resolution);
JSHandle<ResolvedIndexBinding> starResolutionBd = JSHandle<ResolvedIndexBinding>::Cast(starResolution);
if ((!JSTaggedValue::SameValue(thread, resolutionBd->GetModule(thread),
starResolutionBd->GetModule(thread))) ||
resolutionBd->GetIndex() != starResolutionBd->GetIndex()) {
return globalConstants->GetHandledAmbiguousString();
}
}
}
return resolution;
}
template <typename T>
void SourceTextModule::AddExportName(JSThread *thread, const JSTaggedValue &exportEntry,
CVector<std::string> &exportedNames)
{
if (!exportEntry.IsUndefined()) {
JSMutableHandle<T> ee(thread, thread->GlobalConstants()->GetUndefined());
JSHandle<TaggedArray> exportEntries(thread, exportEntry);
size_t exportEntriesLen = exportEntries->GetLength();
for (size_t idx = 0; idx < exportEntriesLen; idx++) {
ee.Update(exportEntries->Get(thread, idx));
std::string exportName = EcmaStringAccessor(ee->GetExportName(thread)).ToStdString(thread);
exportedNames.emplace_back(exportName);
}
}
}
JSHandle<JSTaggedValue> SourceTextModule::ResolveElementOfObject(JSThread *thread,
const JSHandle<JSHClass> &hclass,
const JSHandle<JSTaggedValue> &exportName,
const JSHandle<SourceTextModule> &module)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
int idx = JSHClass::FindPropertyEntry(thread, *hclass, exportName.GetTaggedValue());
if (idx != -1) {
return JSHandle<JSTaggedValue>::Cast(factory->NewResolvedIndexBindingRecord(module, idx));
}
return thread->GlobalConstants()->GetHandledUndefined();
}
JSHandle<JSTaggedValue> SourceTextModule::ResolveLocalExport(JSThread *thread,
const JSHandle<JSTaggedValue> &exportEntry,
const JSHandle<JSTaggedValue> &exportName,
const JSHandle<SourceTextModule> &module)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<LocalExportEntry> ee(thread, thread->GlobalConstants()->GetUndefined());
JSMutableHandle<JSTaggedValue> localName(thread, thread->GlobalConstants()->GetUndefined());
JSHandle<TaggedArray> localExportEntries(exportEntry);
size_t localExportEntriesLen = localExportEntries->GetLength();
for (size_t idx = 0; idx < localExportEntriesLen; idx++) {
ee.Update(localExportEntries->Get(thread, idx));
auto moduleType = module->GetTypes();
if (IsNativeModule(moduleType) || moduleType == ModuleTypes::CJS_MODULE) {
return JSHandle<JSTaggedValue>::Cast(factory->NewResolvedBindingRecord(module, exportName));
}
if ((JSTaggedValue::SameValueString(thread, ee->GetExportName(thread), exportName.GetTaggedValue()))) {
if (module->GetIsNewBcVersion()) {
return JSHandle<JSTaggedValue>::Cast(factory->NewResolvedIndexBindingRecord(module,
ee->GetLocalIndex()));
}
localName.Update(ee->GetLocalName(thread));
return JSHandle<JSTaggedValue>::Cast(factory->NewResolvedBindingRecord(module, localName));
}
}
return thread->GlobalConstants()->GetHandledUndefined();
}
JSHandle<JSTaggedValue> SourceTextModule::ResolveIndirectExport(JSThread *thread,
const JSHandle<JSTaggedValue> &exportEntry,
const JSHandle<JSTaggedValue> &exportName,
const JSHandle<SourceTextModule> &module,
ResolvedMultiMap &resolvedMap)
{
auto globalConstants = thread->GlobalConstants();
JSTaggedValue undefined = globalConstants->GetUndefined();
JSMutableHandle<IndirectExportEntry> ee(thread, undefined);
JSMutableHandle<JSTaggedValue> importName(thread, undefined);
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules(thread));
JSHandle<TaggedArray> indirectExportEntries(exportEntry);
size_t indirectExportEntriesLen = indirectExportEntries->GetLength();
for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) {
ee.Update(indirectExportEntries->Get(thread, idx));
if (JSTaggedValue::SameValueString(thread, exportName.GetTaggedValue(), ee->GetExportName(thread))) {
JSHandle<SourceTextModule> requestedModule =
GetModuleFromCacheOrResolveNewOne(thread, module, requestedModules, ee->GetModuleRequestIndex());
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
ASSERT(requestedModule.GetTaggedValue().IsSourceTextModule());
importName.Update(ee->GetImportName(thread));
return SourceTextModule::ResolveExport(thread, requestedModule, importName, resolvedMap);
}
}
return thread->GlobalConstants()->GetHandledUndefined();
}
void SourceTextModule::CheckResolvedBinding(JSThread *thread, const JSHandle<SourceTextModule> &module)
{
auto globalConstants = thread->GlobalConstants();
JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(thread);
if (indirectExportEntriesTv.IsUndefined()) {
return;
}
JSMutableHandle<IndirectExportEntry> ee(thread, globalConstants->GetUndefined());
JSMutableHandle<JSTaggedValue> exportName(thread, globalConstants->GetUndefined());
JSHandle<TaggedArray> indirectExportEntries(thread, indirectExportEntriesTv);
size_t indirectExportEntriesLen = indirectExportEntries->GetLength();
for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) {
ee.Update(indirectExportEntries->Get(thread, idx));
exportName.Update(ee->GetExportName(thread));
ResolvedMultiMap resolvedMap;
JSHandle<JSTaggedValue> resolution =
SourceTextModule::ResolveExport(thread, module, exportName, resolvedMap);
RETURN_IF_ABRUPT_COMPLETION(thread);
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());
}
ASSERT(resolution->IsResolvedBinding());
}
}
void SourceTextModule::CheckResolvedIndexBinding(JSThread *thread, const JSHandle<SourceTextModule> &module)
{
auto globalConstants = thread->GlobalConstants();
JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(thread);
if (indirectExportEntriesTv.IsUndefined()) {
return;
}
JSMutableHandle<IndirectExportEntry> ee(thread, globalConstants->GetUndefined());
JSMutableHandle<JSTaggedValue> exportName(thread, globalConstants->GetUndefined());
JSHandle<TaggedArray> indirectExportEntries(thread, indirectExportEntriesTv);
size_t indirectExportEntriesLen = indirectExportEntries->GetLength();
for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) {
ee.Update(indirectExportEntries->Get(thread, idx));
exportName.Update(ee->GetExportName(thread));
ResolvedMultiMap resolvedMap;
JSHandle<JSTaggedValue> resolution =
SourceTextModule::ResolveExport(thread, module, exportName, resolvedMap);
RETURN_IF_ABRUPT_COMPLETION(thread);
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::IsAsyncEvaluating()
{
return GetAsyncEvaluatingOrdinal() >= FIRST_ASYNC_EVALUATING_ORDINAL;
}
void SourceTextModule::AddAsyncParentModule(JSThread *thread, JSHandle<SourceTextModule> &module,
JSHandle<SourceTextModule> &parent)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue asyncParentModules = module->GetAsyncParentModules(thread);
if (asyncParentModules.IsUndefined()) {
JSHandle<TaggedArray> array = factory->NewTaggedArray(1);
array->Set(thread, 0, parent.GetTaggedValue());
module->SetAsyncParentModules(thread, array);
} else {
JSHandle<TaggedArray> 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<SourceTextModule> &module,
const void *buffer, size_t size, const ExecuteTypes &executeType)
{
ASSERT(module->GetStatus() == ModuleStatus::EVALUATING || module->GetStatus() == ModuleStatus::EVALUATING_ASYNC);
ASSERT(module->GetHasTLA());
CString moduleFilenameStr = module->GetEcmaModuleFilenameString();
CString entryPoint = module->GetEcmaModuleRecordNameString();
if (entryPoint.empty()) {
entryPoint = JSPandaFile::ENTRY_FUNCTION_NAME;
}
std::shared_ptr<JSPandaFile> 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) {
LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << moduleFilenameStr;
}
Expected<JSTaggedValue, bool> result =
JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entryPoint, executeType);
ASSERT(result.Value().IsJSPromise());
JSHandle<JSPromise> promise(thread, result.Value());
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSAsyncModuleFulfilledFunction> onFulfilled =
factory->CreateJSAsyncModuleFulfilledFunction();
onFulfilled->SetModule(thread, module);
JSHandle<JSAsyncModuleRejectedFunction> onRejected =
factory->CreateJSAsyncModuleRejectedFunction();
onRejected->SetModule(thread, module);
#if ENABLE_LATEST_OPTIMIZATION
builtins::BuiltinsPromise::PerformPromiseThen(
thread, promise, JSHandle<JSTaggedValue>::Cast(onFulfilled),
JSHandle<JSTaggedValue>::Cast(onRejected), thread->GlobalConstants()->GetHandledUndefined());
#else
JSHandle<PromiseCapability> tcap =
JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
RETURN_IF_ABRUPT_COMPLETION(thread);
builtins::BuiltinsPromise::PerformPromiseThen(
thread, promise, JSHandle<JSTaggedValue>::Cast(onFulfilled),
JSHandle<JSTaggedValue>::Cast(onRejected), tcap);
#endif
}
void SourceTextModule::GatherAvailableAncestors(JSThread *thread, const JSHandle<SourceTextModule> &module,
AsyncParentCompletionSet &execList)
{
auto globalConstants = thread->GlobalConstants();
JSTaggedValue asyncParentModulesValue = module->GetAsyncParentModules(thread);
if (asyncParentModulesValue.IsUndefined()) {
return;
}
JSMutableHandle<SourceTextModule> cycleRoot(thread, globalConstants->GetUndefined());
JSHandle<TaggedArray> asyncParentModules(thread, asyncParentModulesValue);
size_t asyncParentModulesLen = asyncParentModules->GetLength();
for (size_t idx = 0; idx < asyncParentModulesLen; idx++) {
JSHandle<SourceTextModule> parentModule(thread, asyncParentModules->Get(thread, idx));
cycleRoot.Update(parentModule->GetCycleRoot(thread));
if (execList.find(parentModule) == execList.end() &&
cycleRoot->GetStatus() != ModuleStatus::ERRORED) {
ASSERT(parentModule->GetStatus() == ModuleStatus::EVALUATING_ASYNC);
ASSERT(parentModule->IsAsyncEvaluating());
ASSERT(parentModule->GetPendingAsyncDependencies() > 0);
parentModule->SetPendingAsyncDependencies(parentModule->GetPendingAsyncDependencies() - 1);
if (parentModule->GetPendingAsyncDependencies() == 0) {
execList.insert(parentModule);
if (!parentModule->GetHasTLA()) {
GatherAvailableAncestors(thread, parentModule, execList);
}
}
}
}
}
void SourceTextModule::AsyncModuleExecutionFulfilled(JSThread *thread, const JSHandle<SourceTextModule> &module)
{
if (module->GetStatus() == ModuleStatus::ERRORED) {
ASSERT(!module->GetException(thread).IsHole());
return;
}
ASSERT(module->GetStatus() == ModuleStatus::EVALUATING_ASYNC);
ASSERT(module->IsAsyncEvaluating());
module->SetAsyncEvaluatingOrdinal(ASYNC_EVALUATE_DID_FINISH);
module->SetStatus(ModuleStatus::EVALUATED);
auto globalConstants = thread->GlobalConstants();
JSTaggedValue topLevelCapabilityValue = module->GetTopLevelCapability(thread);
if (!topLevelCapabilityValue.IsUndefined()) {
ASSERT(JSTaggedValue::SameValue(thread, module->GetCycleRoot(thread), module.GetTaggedValue()));
JSHandle<JSPromise> topLevelCapability(thread, JSPromise::Cast(topLevelCapabilityValue.GetTaggedObject()));
JSHandle<JSTaggedValue> undefinedValue = globalConstants->GetHandledUndefined();
JSPromise::FulfillPromise(thread, topLevelCapability, undefinedValue);
RETURN_IF_ABRUPT_COMPLETION(thread);
}
AsyncParentCompletionSet execList;
GatherAvailableAncestors(thread, module, execList);
for (JSHandle<SourceTextModule> m : execList) {
if (!m->IsAsyncEvaluating()) {
ASSERT(module->GetStatus() == ModuleStatus::ERRORED);
} else if (m->GetHasTLA()) {
ExecuteAsyncModule(thread, m);
} else {
Expected<JSTaggedValue, bool> result = SourceTextModule::ModuleExecution(thread, m);
if (thread->HasPendingException() || !result || result.Value().IsException()) {
AsyncModuleExecutionRejected(thread, m, JSTaggedValue::Exception());
} else {
m->SetStatus(ModuleStatus::EVALUATED);
JSTaggedValue capabilityValue = m->GetTopLevelCapability(thread);
if (!capabilityValue.IsUndefined()) {
ASSERT(JSTaggedValue::SameValue(thread, m->GetCycleRoot(thread), m.GetTaggedValue()));
JSHandle<JSPromise> topLevelCapability(thread,
JSPromise::Cast(topLevelCapabilityValue.GetTaggedObject()));
JSHandle<JSTaggedValue> undefinedValue = globalConstants->GetHandledUndefined();
JSPromise::FulfillPromise(thread, topLevelCapability, undefinedValue);
RETURN_IF_ABRUPT_COMPLETION(thread);
}
}
}
}
}
void SourceTextModule::AsyncModuleExecutionRejected(JSThread *thread, const JSHandle<SourceTextModule> &module,
JSTaggedValue error)
{
if (module->GetStatus() == ModuleStatus::ERRORED) {
ASSERT(!module->GetException(thread).IsHole());
return;
}
ASSERT(module->GetStatus() == ModuleStatus::EVALUATING_ASYNC);
ASSERT(module->IsAsyncEvaluating());
ASSERT(module->GetException(thread).IsHole());
module->SetStatus(ModuleStatus::ERRORED);
module->SetException(thread, error);
auto globalConstants = thread->GlobalConstants();
JSTaggedValue asyncParentModulesValue = module->GetAsyncParentModules(thread);
if (!asyncParentModulesValue.IsUndefined()) {
JSMutableHandle<SourceTextModule> parentModule(thread, globalConstants->GetUndefined());
JSHandle<TaggedArray> 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);
}
}
JSTaggedValue topLevelCapabilityValue = module->GetTopLevelCapability(thread);
if (!topLevelCapabilityValue.IsUndefined()) {
JSHandle<JSTaggedValue> exceptionHandle(thread, error);
if (exceptionHandle->IsJSError()) {
thread->HandleUncaughtException(error);
}
ASSERT(JSTaggedValue::SameValue(thread, module->GetCycleRoot(thread), module.GetTaggedValue()));
JSHandle<JSPromise> 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<JSAsyncModuleFulfilledFunction> fulfilledFunc =
JSHandle<JSAsyncModuleFulfilledFunction>::Cast(base::BuiltinsBase::GetConstructor(argv));
JSHandle<SourceTextModule> module(thread, fulfilledFunc->GetModule(thread));
AsyncModuleExecutionFulfilled(thread, module);
return JSTaggedValue::Undefined();
}
JSTaggedValue SourceTextModule::AsyncModuleRejectedFunc(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSAsyncModuleRejectedFunction> rejectedFunc =
JSHandle<JSAsyncModuleRejectedFunction>::Cast(base::BuiltinsBase::GetConstructor(argv));
JSHandle<SourceTextModule> module(thread, rejectedFunc->GetModule(thread));
[[maybe_unused]] JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, 0);
AsyncModuleExecutionRejected(thread, module, value.GetTaggedValue());
return JSTaggedValue::Undefined();
}
void SourceTextModule::CheckCircularImportTool(JSThread *thread, const CString &circularModuleRecordName,
CList<CString> &referenceList, bool printOtherCircular)
{
referenceList.push_back(circularModuleRecordName);
JSMutableHandle<SourceTextModule> 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<SourceTextModule> &module,
CList<CString> &referenceList,
CString &requiredModuleName, bool printOtherCircular)
{
if (module->GetModuleRequests(thread).IsUndefined()) {
return;
}
auto globalConstants = thread->GlobalConstants();
JSHandle<TaggedArray> moduleRequests(thread, module->GetModuleRequests(thread));
size_t moduleRequestsLen = moduleRequests->GetLength();
JSMutableHandle<JSTaggedValue> required(thread, globalConstants->GetUndefined());
JSMutableHandle<SourceTextModule> requiredModule(thread, globalConstants->GetUndefined());
for (size_t idx = 0; idx < moduleRequestsLen; idx++) {
required.Update(moduleRequests->Get(thread, idx));
requiredModule.Update(JSHandle<SourceTextModule>::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<CString> &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<CString> &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 ------------------------------------------";
}
void SourceTextModule::RecordEvaluatedOrError(JSThread *thread, JSHandle<SourceTextModule> module)
{
if (thread->HasPendingException()) {
module->SetStatus(ModuleStatus::ERRORED);
auto &options = const_cast<EcmaVM *>(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<SourceTextModule> module,
JSTaggedValue exception)
{
if (!IsSharedModule(module)) {
module->SetException(thread, exception);
return;
}
JSHandle<JSTaggedValue> exceptionInfo(thread, exception);
JSHandle<JSTaggedValue> ecmaErrMsg(thread, JSTaggedValue::Undefined());
bool hasPendingException = thread->HasPendingException();
if (hasPendingException) {
thread->ClearExceptionAndExtraErrorMessage();
}
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<JSTaggedValue>::Cast(thread->GetEcmaVM()->GetFactory()->NewFromUtf8(errMsg));
} else {
ecmaErrMsg = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, exceptionInfo));
RETURN_IF_ABRUPT_COMPLETION(thread);
}
if (hasPendingException) {
thread->SetException(exceptionInfo.GetTaggedValue());
}
module->SetException(thread, ecmaErrMsg.GetTaggedValue());
return;
}
bool SourceTextModule::CheckAndThrowModuleError(JSThread *thread)
{
if (GetStatus() == ModuleStatus::ERRORED) {
LOG_FULL(ERROR) << "Error found in module:" << GetEcmaModuleRecordNameString();
JSHandle<JSTaggedValue> exceptionInfo(thread, GetException(thread));
if (exceptionInfo->IsJSError()) {
base::ErrorHelper::PrintJSErrorInfo(thread, exceptionInfo);
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, exceptionInfo.GetTaggedValue(), false);
}
JSHandle<EcmaString> 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<JSHandle<SourceTextModule>> &stack, JSHandle<JSTaggedValue> exception)
{
auto &options = const_cast<EcmaVM *>(thread->GetEcmaVM())->GetJSOptions();
if (options.EnableModuleException()) {
JSTaggedValue::DesensitizedDump(thread, exception);
}
for (auto &mm : stack) {
ASSERT(mm->GetStatus() == ModuleStatus::EVALUATING);
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<JSHandle<SourceTextModule>> &errorStack)
{
auto &options = const_cast<EcmaVM *>(thread->GetEcmaVM())->GetJSOptions();
for (auto &mm : errorStack) {
ASSERT(mm->GetStatus() == ModuleStatus::EVALUATING ||
mm->GetStatus() == ModuleStatus::EVALUATED);
mm->SetStatus(ModuleStatus::ERRORED);
if (options.EnableModuleException()) {
LOG_FULL(INFO) << "Error module: " << mm->GetEcmaModuleRecordNameString();
}
}
}
JSHandle<SourceTextModule> SourceTextModule::GetModuleFromCacheOrResolveNewOne(JSThread *thread,
const JSHandle<SourceTextModule> module, const JSHandle<TaggedArray> requestedModules, uint32_t idx)
{
JSHandle<JSTaggedValue> request(thread, requestedModules->Get(thread, idx));
JSHandle<SourceTextModule> requireModule;
if (!request->IsHole()) {
if (request->IsSourceTextModule()) {
return JSHandle<SourceTextModule>::Cast(request);
}
ModuleManager *moduleManager = thread->GetModuleManager();
CString requestStr = ModulePathHelper::Utf8ConvertToString(thread, request.GetTaggedValue());
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<TaggedArray> moduleRequests(thread, module->GetModuleRequests(thread));
JSHandle<JSTaggedValue> required(thread, moduleRequests->Get(thread, idx));
JSHandle<JSTaggedValue> requiredModule = ModuleResolver::HostResolveImportedModule(thread, module, required);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(SourceTextModule, thread);
SetRequestedModules(thread, requestedModules, idx, requiredModule, SourceTextModule::IsSharedModule(module));
return JSHandle<SourceTextModule>::Cast(requiredModule);
}
JSHandle<JSTaggedValue> SourceTextModule::GetRequestedModuleMayThrowError(JSThread *thread,
const JSHandle<SourceTextModule> module, uint32_t idx, const JSHandle<TaggedArray> requestedModules,
JSHandle<JSTaggedValue> exception)
{
JSHandle<JSTaggedValue> request(thread, requestedModules->Get(thread, idx));
if (UNLIKELY(request->IsHole())) {
LOG_ECMA(ERROR) << "GetRequestedModuleMayThrowError request module is hole";
thread->SetException(exception.GetTaggedValue());
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
}
return JSHandle<JSTaggedValue>::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<TaggedArray> requestedModules, uint32_t idx,
JSHandle<JSTaggedValue> requiredModule, bool isShared)
{
if (!isShared && !IsSharedModule(JSHandle<SourceTextModule>::Cast(requiredModule))) {
requestedModules->Set(thread, idx, requiredModule.GetTaggedValue());
} else {
CString recordName = GetModuleName(requiredModule.GetTaggedValue());
JSHandle<EcmaString> requireModuleName =
thread->GetEcmaVM()->GetFactory()->NewFromUtf8(recordName);
requestedModules->Set(thread, idx, requireModuleName.GetTaggedValue());
}
}
JSHandle<JSTaggedValue> SourceTextModule::CreateBindingByIndexBinding(JSThread* thread,
JSHandle<ResolvedIndexBinding> binding,
bool isShared)
{
JSHandle<SourceTextModule> resolvedModule(thread, binding->GetModule(thread));
JSHandle<JSTaggedValue> bindingName =
GetBindingNameByIndex(thread, resolvedModule, binding->GetIndex());
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
if (isShared) {
return JSHandle<JSTaggedValue>::Cast(factory->NewSResolvedBindingRecord(resolvedModule, bindingName));
}
return JSHandle<JSTaggedValue>::Cast(factory->NewResolvedBindingRecord(resolvedModule, bindingName));
}
JSHandle<JSTaggedValue> SourceTextModule::FindFuncInModuleForHook(JSThread* thread, const std::string &recordName,
const std::string &namespaceName,
const std::string &className,
const std::string &funcName)
{
DisallowGarbageCollection no_gc;
JSHandle<JSTaggedValue> 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<EcmaString> keyHandle = factory->NewFromASCII(keyStr.c_str());
result = module->GetModuleValue(thread, keyHandle.GetTaggedValue(), false);
}
JSHandle<JSTaggedValue> exportEntity(thread, result);
if (exportEntity->IsUndefined() || exportEntity->IsHole()) {
return functionNotFound;
}
return JSObject::FindFuncInObjectForHook(thread, exportEntity, className, funcName);
}
JSHandle<SourceTextModule> SourceTextModule::LoadJsonModule(JSThread *thread, const JSPandaFile *jsPandaFile,
const CString &filename, CString recordName)
{
JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(
ModuleDataExtractor::ParseJsonModule(thread, jsPandaFile, filename));
EvaluateJsonModule(thread, module, jsPandaFile, recordName);
return module;
}
void SourceTextModule::EvaluateJsonModule(JSThread *thread, JSHandle<SourceTextModule> module,
const JSPandaFile *jsPandaFile, CString recordName)
{
JSTaggedValue jsonData = ModuleDataExtractor::JsonParse(thread, jsPandaFile, recordName);
RETURN_IF_ABRUPT_COMPLETION(thread);
SourceTextModule::StoreModuleValue(thread, module, 0, JSHandle<JSTaggedValue>(thread, jsonData));
RETURN_IF_ABRUPT_COMPLETION(thread);
module->SetStatus(ModuleStatus::EVALUATED);
}
}