support top-level await for module

支持ECMA2022规范模块顶层await

Issue: #I8LMRX

Signed-off-by: lichenshuai <lichenshuai@huawei.com>
Change-Id: Id09ac6f6bc6df9d4790d33f998b2b87586650658
This commit is contained in:
lichenshuai 2023-12-09 22:26:03 +08:00
parent b3ce5f1410
commit b21f1eae7c
26 changed files with 848 additions and 119 deletions

View File

@ -2343,6 +2343,18 @@ void Builtins::InitializeForPromiseFuncClass(const JSHandle<GlobalEnv> &env)
promiseExecutorFuncClass->SetExtensible(true);
env->SetPromiseExecutorFunctionClass(thread_, promiseExecutorFuncClass);
JSHandle<JSHClass> asyncModuleFulfilledFuncClass = factory_->NewEcmaHClass(
JSAsyncModuleFulfilledFunction::SIZE, JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION, env->GetFunctionPrototype());
asyncModuleFulfilledFuncClass->SetCallable(true);
asyncModuleFulfilledFuncClass->SetExtensible(true);
env->SetAsyncModuleFulfilledFunctionClass(thread_, asyncModuleFulfilledFuncClass);
JSHandle<JSHClass> asyncModuleRejectedFuncClass = factory_->NewEcmaHClass(
JSAsyncModuleRejectedFunction::SIZE, JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION, env->GetFunctionPrototype());
asyncModuleRejectedFuncClass->SetCallable(true);
asyncModuleRejectedFuncClass->SetExtensible(true);
env->SetAsyncModuleRejectedFunctionClass(thread_, asyncModuleRejectedFuncClass);
JSHandle<JSHClass> promiseAllResolveElementFunctionClass =
factory_->NewEcmaHClass(JSPromiseAllResolveElementFunction::SIZE,
JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION, env->GetFunctionPrototype());

View File

@ -43,6 +43,8 @@ enum class MethodIndex : uint8_t {
BUILTINS_ASYNC_GENERATOR_NEXT_FULFILLED_FUNCTION,
BUILTINS_ASYNC_GENERATOR_NEXT_REJECTED_FUNCTION,
BUILTINS_ASYNC_FROM_SYNC_ITERATOR_FUNCTION,
BUILTINS_ASYNC_MODULE_FULFILLED_FUNCTION,
BUILTINS_ASYNC_MODULE_REJECTED_FUNCTION,
METHOD_END
};
} // namespace panda::ecmascript

View File

@ -394,6 +394,10 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry)
return GetString("PromiseReactionsFunction");
case JSType::JS_PROMISE_EXECUTOR_FUNCTION:
return GetString("PromiseExecutorFunction");
case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION:
return GetString("AsyncModuleFulfilledFunction");
case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION:
return GetString("AsyncModuleRejectedFunction");
case JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION:
return GetString("AsyncFromSyncIterUnwarpFunction");
case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION:

View File

@ -289,6 +289,10 @@ CString JSHClass::DumpJSType(JSType type)
return "PromiseReactionsFunction";
case JSType::JS_PROMISE_EXECUTOR_FUNCTION:
return "PromiseExecutorFunction";
case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION:
return "AsyncModuleFulfilledFunction";
case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION:
return "AsyncModuleRejectedFunction";
case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION:
return "PromiseAllResolveElementFunction";
case JSType::JS_PROMISE_ANY_REJECT_ELEMENT_FUNCTION:
@ -822,6 +826,12 @@ static void DumpObject(TaggedObject *obj, std::ostream &os)
case JSType::JS_PROMISE_EXECUTOR_FUNCTION:
JSPromiseExecutorFunction::Cast(obj)->Dump(os);
break;
case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION:
JSAsyncModuleFulfilledFunction::Cast(obj)->Dump(os);
break;
case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION:
JSAsyncModuleRejectedFunction::Cast(obj)->Dump(os);
break;
case JSType::ASYNC_GENERATOR_REQUEST:
AsyncGeneratorRequest::Cast(obj)->Dump(os);
break;
@ -2715,6 +2725,20 @@ void JSPromiseExecutorFunction::Dump(std::ostream &os) const
JSObject::Dump(os);
}
void JSAsyncModuleFulfilledFunction::Dump(std::ostream &os) const
{
os << " - module: ";
GetModule().Dump(os);
JSObject::Dump(os);
}
void JSAsyncModuleRejectedFunction::Dump(std::ostream &os) const
{
os << " - module: ";
GetModule().Dump(os);
JSObject::Dump(os);
}
void JSPromiseAllResolveElementFunction::Dump(std::ostream &os) const
{
os << " - index: ";
@ -3556,6 +3580,24 @@ void SourceTextModule::Dump(std::ostream &os) const
os << " - NameDictionary: ";
GetNameDictionary().Dump(os);
os << "\n";
os << " - CycleRoot: ";
GetCycleRoot().Dump(os);
os << "\n";
os << " - TopLevelCapability: ";
GetTopLevelCapability().Dump(os);
os << "\n";
os << " - AsyncParentModules: ";
GetAsyncParentModules().Dump(os);
os << "\n";
os << " - HasTLA: ";
os << GetHasTLA();
os << "\n";
os << " - AsyncEvaluatingOrdinal: ";
os << GetAsyncEvaluatingOrdinal();
os << "\n";
os << " - PendingAsyncDependencies: ";
os << GetPendingAsyncDependencies();
os << "\n";
}
void ImportEntry::Dump(std::ostream &os) const
@ -3926,6 +3968,12 @@ static void DumpObject(TaggedObject *obj, std::vector<Reference> &vec, bool isVm
case JSType::JS_PROMISE_EXECUTOR_FUNCTION:
JSPromiseExecutorFunction::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION:
JSAsyncModuleFulfilledFunction::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION:
JSAsyncModuleRejectedFunction::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::ASYNC_GENERATOR_REQUEST:
AsyncGeneratorRequest::Cast(obj)->DumpForSnapshot(vec);
return;
@ -5081,6 +5129,18 @@ void JSPromiseExecutorFunction::DumpForSnapshot(std::vector<Reference> &vec) con
JSObject::DumpForSnapshot(vec);
}
void JSAsyncModuleFulfilledFunction::DumpForSnapshot(std::vector<Reference> &vec) const
{
vec.emplace_back(CString("module"), GetModule());
JSObject::DumpForSnapshot(vec);
}
void JSAsyncModuleRejectedFunction::DumpForSnapshot(std::vector<Reference> &vec) const
{
vec.emplace_back(CString("module"), GetModule());
JSObject::DumpForSnapshot(vec);
}
void JSPromiseAllResolveElementFunction::DumpForSnapshot(std::vector<Reference> &vec) const
{
vec.emplace_back(CString("index"), GetIndex());
@ -5501,6 +5561,12 @@ void SourceTextModule::DumpForSnapshot(std::vector<Reference> &vec) const
vec.emplace_back(CString("DFSIndex"), JSTaggedValue(GetDFSIndex()));
vec.emplace_back(CString("DFSAncestorIndex"), JSTaggedValue(GetDFSAncestorIndex()));
vec.emplace_back(CString("NameDictionary"), GetNameDictionary());
vec.emplace_back(CString("CycleRoot"), GetCycleRoot());
vec.emplace_back(CString("TopLevelCapability"), GetTopLevelCapability());
vec.emplace_back(CString("AsyncParentModules"), GetAsyncParentModules());
vec.emplace_back(CString("HasTLA"), JSTaggedValue(GetHasTLA()));
vec.emplace_back(CString("AsyncEvaluatingOrdinal"), JSTaggedValue(GetAsyncEvaluatingOrdinal()));
vec.emplace_back(CString("PendingAsyncDependencies"), JSTaggedValue(GetPendingAsyncDependencies()));
}
void ImportEntry::DumpForSnapshot(std::vector<Reference> &vec) const

View File

@ -298,11 +298,13 @@ Expected<JSTaggedValue, bool> EcmaContext::CommonInvokeEcmaEntrypoint(const JSPa
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
EcmaRuntimeStatScope runtimeStatScope(vm_);
EcmaInterpreter::Execute(info);
result = EcmaInterpreter::Execute(info);
}
}
if (!thread_->HasPendingException()) {
job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
if (!jsPandaFile->IsModule(recordInfo)) {
job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
}
}
return result;

View File

@ -584,7 +584,9 @@ void *EcmaVM::InternalMethodTable[] = {
reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::throwerFunction),
reinterpret_cast<void *>(JSAsyncGeneratorObject::ProcessorFulfilledFunc),
reinterpret_cast<void *>(JSAsyncGeneratorObject::ProcessorRejectedFunc),
reinterpret_cast<void *>(JSAsyncFromSyncIterator::AsyncFromSyncIterUnwarpFunction)
reinterpret_cast<void *>(JSAsyncFromSyncIterator::AsyncFromSyncIterUnwarpFunction),
reinterpret_cast<void *>(SourceTextModule::AsyncModuleFulfilledFunc),
reinterpret_cast<void *>(SourceTextModule::AsyncModuleRejectedFunc)
};
void EcmaVM::GenerateInternalNativeMethods()

View File

@ -76,6 +76,7 @@ template<typename T>
class JSHandle;
class JSArrayBuffer;
class JSFunction;
class SourceTextModule;
class Program;
class TSManager;
class AOTFileManager;

View File

@ -163,6 +163,8 @@
V(JSTaggedValue, AsyncAwaitStatusFunctionClass, ASYNC_AWAIT_STATUS_FUNCTION_CLASS) \
V(JSTaggedValue, PromiseReactionFunctionClass, PROMISE_REACTION_FUNCTION_CLASS) \
V(JSTaggedValue, PromiseExecutorFunctionClass, PROMISE_EXECUTOR_FUNCTION_CLASS) \
V(JSTaggedValue, AsyncModuleFulfilledFunctionClass, ASYNC_MODULE_FULFILLED_FUNCTION_CLASS) \
V(JSTaggedValue, AsyncModuleRejectedFunctionClass, ASYNC_MODULE_REJECTED_FUNCTION_CLASS) \
V(JSTaggedValue, GeneratorFunctionClass, GENERATOR_FUNCTION_CLASS) \
V(JSTaggedValue, AsyncGeneratorFunctionClass, ASYNC_GENERATOR_FUNCTION_CLASS) \
V(JSTaggedValue, PromiseAllResolveElementFunctionClass, PROMISE_ALL_RESOLVE_ELEMENT_FUNC_CLASS) \

View File

@ -321,6 +321,30 @@ public:
DECL_DUMP()
};
class JSAsyncModuleFulfilledFunction : public JSFunction {
public:
CAST_CHECK(JSAsyncModuleFulfilledFunction, IsJSAsyncModuleFulfilledFunction);
static constexpr size_t MODULE_OFFSET = JSFunction::SIZE;
ACCESSORS(Module, MODULE_OFFSET, SIZE);
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, MODULE_OFFSET, SIZE)
DECL_DUMP()
};
class JSAsyncModuleRejectedFunction : public JSFunction {
public:
CAST_CHECK(JSAsyncModuleRejectedFunction, IsJSAsyncModuleRejectedFunction);
static constexpr size_t MODULE_OFFSET = JSFunction::SIZE;
ACCESSORS(Module, MODULE_OFFSET, SIZE);
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, MODULE_OFFSET, SIZE)
DECL_DUMP()
};
class JSPromiseAllResolveElementFunction : public JSFunction {
public:
CAST_CHECK(JSPromiseAllResolveElementFunction, IsJSPromiseAllResolveElementFunction);

View File

@ -81,6 +81,8 @@ struct Reference;
JS_PROXY_REVOC_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \
JS_PROMISE_REACTIONS_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \
JS_PROMISE_EXECUTOR_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \
JS_ASYNC_MODULE_FULFILLED_FUNCTION, /* ////////////////////////////////////////////////////////////-PADDING */ \
JS_ASYNC_MODULE_REJECTED_FUNCTION, /* /////////////////////////////////////////////////////////////-PADDING */ \
JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION, /* //////////////////////////////////////////////////////-PADDING */ \
JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION, /* //////////////////////////////////////////////////////-PADDING */ \
JS_ASYNC_GENERATOR_RESUME_NEXT_RETURN_PROCESSOR_RST_FTN, /* ///////////////////////////////////////-PADDING */ \
@ -757,6 +759,16 @@ public:
return GetObjectType() == JSType::JS_PROMISE_EXECUTOR_FUNCTION;
}
inline bool IsJSAsyncModuleFulfilledFunction() const
{
return GetObjectType() == JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION;
}
inline bool IsJSAsyncModuleRejectedFunction() const
{
return GetObjectType() == JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION;
}
inline bool IsJSAsyncFromSyncIterUnwarpFunction() const
{
return GetObjectType() == JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION;

View File

@ -622,6 +622,16 @@ inline bool JSTaggedValue::IsJSPromiseExecutorFunction() const
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromiseExecutorFunction();
}
inline bool JSTaggedValue::IsJSAsyncModuleFulfilledFunction() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncModuleFulfilledFunction();
}
inline bool JSTaggedValue::IsJSAsyncModuleRejectedFunction() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncModuleRejectedFunction();
}
inline bool JSTaggedValue::IsJSAsyncFromSyncIterUnwarpFunction() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncFromSyncIterUnwarpFunction();

View File

@ -544,6 +544,8 @@ public:
bool IsProgram() const;
bool IsJSPromiseReactionFunction() const;
bool IsJSPromiseExecutorFunction() const;
bool IsJSAsyncModuleFulfilledFunction() const;
bool IsJSAsyncModuleRejectedFunction() const;
bool IsJSAsyncFromSyncIterUnwarpFunction() const;
bool IsJSPromiseAllResolveElementFunction() const;
bool IsJSAsyncGeneratorResNextRetProRstFtn() const;

View File

@ -156,6 +156,9 @@ void JSPandaFile::InitializeUnMergedPF()
info.classId = index;
info.isCjs = true;
}
if (!info.hasTopLevelAwait && std::strcmp(HASTLA_CLASS, desc) == 0) {
info.hasTopLevelAwait = true;
}
}
jsRecordInfo_.insert({JSPandaFile::ENTRY_FUNCTION_NAME, info});
methodLiterals_ =
@ -190,6 +193,8 @@ void JSPandaFile::InitializeMergedPF()
info.jsonStringId = fieldAccessor.GetValue<uint32_t>().value();
} else if (std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) {
info.moduleRecordIdx = fieldAccessor.GetValue<int32_t>().value();
} else if (std::strcmp(HAS_TOP_LEVEL_AWAIT, fieldName) == 0) {
info.hasTopLevelAwait = fieldAccessor.GetValue<bool>().value();
} else if (std::strcmp(TYPE_FLAG, fieldName) == 0) {
info.hasTSTypes = fieldAccessor.GetValue<uint8_t>().value() != 0;
} else if (std::strcmp(TYPE_SUMMARY_OFFSET, fieldName) == 0) {

View File

@ -37,6 +37,7 @@ public:
int jsonStringId {-1};
CUnorderedSet<const EcmaVM *> vmListOfParsedConstPool;
int moduleRecordIdx {-1};
bool hasTopLevelAwait {false};
CUnorderedMap<uint32_t, uint64_t> constpoolMap;
bool hasTSTypes {false};
uint32_t typeSummaryOffset {0};
@ -65,12 +66,14 @@ public:
static constexpr char MODULE_CLASS[] = "L_ESModuleRecord;";
static constexpr char COMMONJS_CLASS[] = "L_CommonJsRecord;";
static constexpr char HASTLA_CLASS[] = "L_HasTopLevelAwait;";
static constexpr char TYPE_FLAG[] = "typeFlag";
static constexpr char TYPE_SUMMARY_OFFSET[] = "typeSummaryOffset";
static constexpr char IS_COMMON_JS[] = "isCommonjs";
static constexpr char IS_JSON_CONTENT[] = "jsonFileContent";
static constexpr char MODULE_RECORD_IDX[] = "moduleRecordIdx";
static constexpr char HAS_TOP_LEVEL_AWAIT[] = "hasTopLevelAwait";
static constexpr char PACKAGE_NAME[] = "pkgName@";
static constexpr char MERGE_ABC_NAME[] = "modules.abc";
static constexpr char NPM_PATH_SEGMENT[] = "node_modules";
@ -201,6 +204,18 @@ public:
return -1;
}
int GetHasTopLevelAwait(const CString &recordName = ENTRY_FUNCTION_NAME) const
{
if (IsBundlePack()) {
return jsRecordInfo_.begin()->second.hasTopLevelAwait;
}
auto info = jsRecordInfo_.find(recordName);
if (info != jsRecordInfo_.end()) {
return info->second.hasTopLevelAwait;
}
return false;
}
Span<const uint32_t> GetClasses() const
{
return pf_->GetClasses();

View File

@ -87,7 +87,6 @@ HWTEST_F_L0(JSPandaFileExecutorTest, Execute)
Expected<JSTaggedValue, bool> result =
JSPandaFileExecutor::Execute(thread, pf.get(), JSPandaFile::ENTRY_MAIN_FUNCTION);
EXPECT_TRUE(result);
EXPECT_EQ(result.Value(), JSTaggedValue::Hole());
pfManager->RemoveJSPandaFileVm(instance, pf.get());
}
@ -123,7 +122,6 @@ HWTEST_F_L0(JSPandaFileExecutorTest, ExecuteFromFile)
Expected<JSTaggedValue, bool> result =
JSPandaFileExecutor::ExecuteFromFile(thread, CString(fileName), JSPandaFile::ENTRY_MAIN_FUNCTION);
EXPECT_TRUE(result);
EXPECT_EQ(result.Value(), JSTaggedValue::Hole());
pfManager->RemoveJSPandaFileVm(instance, pf.get());
std::shared_ptr<JSPandaFile> foundPf = pfManager->FindJSPandaFile(fileName);
@ -161,7 +159,6 @@ HWTEST_F_L0(JSPandaFileExecutorTest, ExecuteFromBuffer)
Expected<JSTaggedValue, bool> result = JSPandaFileExecutor::ExecuteFromBuffer(
thread, (void *)data, sizeof(data), JSPandaFile::ENTRY_MAIN_FUNCTION, CString(fileName));
EXPECT_TRUE(result);
EXPECT_EQ(result.Value(), JSTaggedValue::Hole());
pfManager->RemoveJSPandaFileVm(instance, pf.get());
std::shared_ptr<JSPandaFile> foundPf = pfManager->FindJSPandaFile(fileName);

View File

@ -183,6 +183,16 @@ public:
jsPromiseExecutorFunction->VisitRangeSlot<visitType>(visitor);
break;
}
case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION: {
auto jsAsyncModuleFulfilledFunction = JSAsyncModuleFulfilledFunction::Cast(object);
jsAsyncModuleFulfilledFunction->VisitRangeSlot<visitType>(visitor);
break;
}
case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION: {
auto jsAsyncModuleRejectedFunction = JSAsyncModuleRejectedFunction::Cast(object);
jsAsyncModuleRejectedFunction->VisitRangeSlot<visitType>(visitor);
break;
}
case JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION: {
auto jsAsyncFromSyncIterUnwarpFunction = JSAsyncFromSyncIterUnwarpFunction::Cast(object);
jsAsyncFromSyncIterUnwarpFunction->VisitRangeSlot<visitType>(visitor);

View File

@ -90,6 +90,12 @@ public:
static CString GetRecordName(JSTaggedValue module);
static int GetExportObjectIndex(EcmaVM *vm, JSHandle<SourceTextModule> ecmaModule, const std::string &key);
uint32_t NextModuleAsyncEvaluatingOrdinal()
{
uint32_t ordinal = nextModuleAsyncEvaluatingOrdinal_++;
return ordinal;
}
private:
NO_COPY_SEMANTIC(ModuleManager);
NO_MOVE_SEMANTIC(ModuleManager);
@ -120,6 +126,8 @@ private:
static constexpr uint32_t DEAULT_DICTIONART_CAPACITY = 4;
uint32_t nextModuleAsyncEvaluatingOrdinal_{SourceTextModule::FIRST_ASYNC_EVALUATING_ORDINAL};
EcmaVM *vm_ {nullptr};
JSTaggedValue resolvedModules_ {JSTaggedValue::Hole()};
JSTaggedValue cachedEmptyModule_ {JSTaggedValue::Hole()};

View File

@ -27,7 +27,7 @@ int32_t ModuleRecord::Instantiate(JSThread *thread, const JSHandle<JSTaggedValue
UNREACHABLE();
}
int32_t ModuleRecord::Evaluate(JSThread *thread, const JSHandle<JSTaggedValue> &module)
JSTaggedValue ModuleRecord::Evaluate(JSThread *thread, const JSHandle<JSTaggedValue> &module)
{
if (module->IsSourceTextModule()) {
JSHandle<SourceTextModule> moduleRecord = JSHandle<SourceTextModule>::Cast(module);

View File

@ -27,7 +27,7 @@ public:
// 15.2.1.16.4 Instantiate()
static int Instantiate(JSThread *thread, const JSHandle<JSTaggedValue> &module);
// 15.2.1.16.5 Evaluate()
static int Evaluate(JSThread *thread, const JSHandle<JSTaggedValue> &module);
static JSTaggedValue Evaluate(JSThread *thread, const JSHandle<JSTaggedValue> &module);
static JSTaggedValue GetNamespace(JSTaggedValue module);
static void SetNamespace(JSThread *thread, JSTaggedValue module, JSTaggedValue value);

View File

@ -16,10 +16,14 @@
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/global_env.h"
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/base/path_helper.h"
#include "ecmascript/base/string_helper.h"
#include "ecmascript/builtins/builtins_promise.h"
#include "ecmascript/jobs/micro_job_queue.h"
#include "ecmascript/jspandafile/js_pandafile_executor.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/js_promise.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/module/js_module_deregister.h"
#include "ecmascript/module/js_module_manager.h"
@ -486,8 +490,10 @@ int SourceTextModule::Instantiate(JSThread *thread, const JSHandle<JSTaggedValue
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX);
JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleHdl);
// 1. Let module be this Source Text Module Record.
// 2. Assert: module.[[Status]] is not "instantiating" or "evaluating".
ASSERT(module->GetStatus() != ModuleStatus::INSTANTIATING && module->GetStatus() != ModuleStatus::EVALUATING);
// 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);
// 3. Let stack be a new empty List.
CVector<JSHandle<SourceTextModule>> stack;
// 4. Let result be InnerModuleInstantiation(module, stack, 0).
@ -497,8 +503,10 @@ int SourceTextModule::Instantiate(JSThread *thread, const JSHandle<JSTaggedValue
if (thread->HasPendingException()) {
return HandleInstantiateException(module, stack, result);
}
// 6. Assert: module.[[Status]] is "instantiated" or "evaluated".
ASSERT(module->GetStatus() == ModuleStatus::INSTANTIATED || module->GetStatus() == ModuleStatus::EVALUATED);
// 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);
// 7. Assert: stack is empty.
ASSERT(stack.empty());
// 8. Return undefined.
@ -519,8 +527,10 @@ int SourceTextModule::InstantiateForConcurrent(JSThread *thread, const JSHandle<
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX);
JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleHdl);
// 1. Let module be this Source Text Module Record.
// 2. Assert: module.[[Status]] is not "instantiating" or "evaluating".
ASSERT(module->GetStatus() != ModuleStatus::INSTANTIATING && module->GetStatus() != ModuleStatus::EVALUATING);
// 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);
// 3. Let stack be a new empty List.
CVector<JSHandle<SourceTextModule>> stack;
// 4. Let result be InnerModuleInstantiation(module, stack, 0).
@ -530,8 +540,10 @@ int SourceTextModule::InstantiateForConcurrent(JSThread *thread, const JSHandle<
if (thread->HasPendingException()) {
return HandleInstantiateException(module, stack, result);
}
// 6. Assert: module.[[Status]] is "instantiated" or "evaluated".
ASSERT(module->GetStatus() == ModuleStatus::INSTANTIATED || module->GetStatus() == ModuleStatus::EVALUATED);
// 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);
// 7. Assert: stack is empty.
ASSERT(stack.empty());
// 8. Return undefined.
@ -595,10 +607,12 @@ std::optional<int> SourceTextModule::HandleInnerModuleInstantiation(JSThread *th
index = SourceTextModule::InnerModuleInstantiation(thread,
requiredModuleRecord, stack, index, excuteFromJob);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
// c. Assert: requiredModule.[[Status]] is either "instantiating", "instantiated", or "evaluated".
// c. Assert: requiredModule.[[Status]] is one of LINKING, LINKED, EVALUATING-ASYNC, or EVALUATED.
ModuleStatus requiredModuleStatus = requiredModule->GetStatus();
ASSERT((requiredModuleStatus == ModuleStatus::INSTANTIATING ||
requiredModuleStatus == ModuleStatus::INSTANTIATED || requiredModuleStatus == ModuleStatus::EVALUATED));
ASSERT(requiredModuleStatus == ModuleStatus::INSTANTIATING ||
requiredModuleStatus == ModuleStatus::INSTANTIATED ||
requiredModuleStatus == ModuleStatus::EVALUATING_ASYNC ||
requiredModuleStatus == ModuleStatus::EVALUATED);
// 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) {
@ -625,10 +639,11 @@ int SourceTextModule::InnerModuleInstantiation(JSThread *thread, const JSHandle<
return index;
}
JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
// 2. If module.[[Status]] is "instantiating", "instantiated", or "evaluated", then 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 ||
status == ModuleStatus::INSTANTIATED ||
status == ModuleStatus::EVALUATING_ASYNC ||
status == ModuleStatus::EVALUATED) {
return index;
}
@ -682,10 +697,11 @@ int SourceTextModule::ModuleInstantiation(JSThread *thread, const JSHandle<Modul
return index;
}
JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
// 2. If module.[[Status]] is "instantiating", "instantiated", or "evaluated", then 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 ||
status == ModuleStatus::INSTANTIATED ||
status == ModuleStatus::EVALUATING_ASYNC ||
status == ModuleStatus::EVALUATED) {
return index;
}
@ -907,13 +923,8 @@ JSHandle<JSTaggedValue> SourceTextModule::GetModuleNamespace(JSThread *thread,
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".
ModuleStatus status = module->GetStatus();
ASSERT(status != ModuleStatus::UNINSTANTIATED);
// 3. Assert: If module.[[Status]] is "evaluated", module.[[EvaluationError]] is undefined.
if (status == ModuleStatus::EVALUATED) {
ASSERT(module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
}
// 4. Let namespace be module.[[Namespace]].
ASSERT(module->GetStatus() != ModuleStatus::UNINSTANTIATED);
// 3. Let namespace be module.[[Namespace]].
JSMutableHandle<JSTaggedValue> moduleNamespace(thread, module->GetNamespace().GetWeakRawValue());
// If namespace is undefined, then
if (moduleNamespace->IsUndefined()) {
@ -945,20 +956,12 @@ JSHandle<JSTaggedValue> SourceTextModule::GetModuleNamespace(JSThread *thread,
return moduleNamespace;
}
int SourceTextModule::Evaluate(JSThread *thread, const JSHandle<SourceTextModule> &module,
const void *buffer, size_t size, bool excuteFromJob)
void SourceTextModule::HandleEvaluateResult(JSThread *thread, JSHandle<SourceTextModule> &module,
JSHandle<PromiseCapability> &capability, const CVector<JSHandle<SourceTextModule>> &stack, int result)
{
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SourceTextModule::Evaluate");
// 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));
// 3. Let stack be a new empty List.
CVector<JSHandle<SourceTextModule>> stack;
// 4. Let result be InnerModuleEvaluation(module, stack, 0)
JSHandle<ModuleRecord> moduleRecord = JSHandle<ModuleRecord>::Cast(module);
int result = SourceTextModule::InnerModuleEvaluation(thread, moduleRecord, stack, 0, buffer, size, excuteFromJob);
// 5. If result is an abrupt completion, then
ModuleStatus status;
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
// 9. If result is an abrupt completion, then
if (thread->HasPendingException()) {
// a. For each module m in stack, do
for (auto mm : stack) {
@ -972,16 +975,68 @@ int SourceTextModule::Evaluate(JSThread *thread, const JSHandle<SourceTextModule
// b. Assert: module.[[Status]] is "evaluated" and module.[[EvaluationError]] is result.
status = module->GetStatus();
ASSERT(status == ModuleStatus::EVALUATED && module->GetEvaluationError() == result);
// c. return result
return result;
//d. Perform ! Call(capability.[[Reject]], undefined, « result.[[Value]] »).
JSHandle<JSTaggedValue> reject(thread, capability->GetReject());
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
RETURN_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(JSTaggedValue(result));
[[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
RETURN_IF_ABRUPT_COMPLETION(thread);
// 10. Else,
} else {
// 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(module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
// c. If module.[[AsyncEvaluation]] is false, then
// i. Assert: module.[[Status]] is EVALUATED.
// ii. Perform ! Call(capability.[[Resolve]], undefined, « undefined »).
if (!module->IsAsyncEvaluating()) {
ASSERT(status == ModuleStatus::EVALUATED);
}
// d. Assert: stack is empty.
ASSERT(stack.empty());
}
// 6. Assert: module.[[Status]] is "evaluated" and module.[[EvaluationError]] is undefined.
status = module->GetStatus();
ASSERT(status == ModuleStatus::EVALUATED && module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
// 7. Assert: stack is empty.
ASSERT(stack.empty());
// 8. Return undefined.
return SourceTextModule::UNDEFINED_INDEX;
}
JSTaggedValue SourceTextModule::Evaluate(JSThread *thread, const JSHandle<SourceTextModule> &moduleHdl,
const void *buffer, size_t size, bool excuteFromJob)
{
ECMA_BYTRACE_NAME(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<SourceTextModule> 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());
}
// 4. If module.[[TopLevelCapability]] is not EMPTY, then
// a. Return module.[[TopLevelCapability]].[[Promise]].
// 5. Let stack be a new empty List.
CVector<JSHandle<SourceTextModule>> stack;
// 6. Let capability be ! NewPromiseCapability(%Promise%).
auto vm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
JSHandle<PromiseCapability> capability =
JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 7. Set module.[[TopLevelCapability]] to capability.
module->SetTopLevelCapability(thread, capability);
// 8. Let result be Completion(InnerModuleEvaluation(module, stack, 0)).
JSHandle<ModuleRecord> moduleRecord = JSHandle<ModuleRecord>::Cast(module);
int result = SourceTextModule::InnerModuleEvaluation(thread, moduleRecord, stack, 0, buffer, size, excuteFromJob);
HandleEvaluateResult(thread, module, capability, stack, result);
if (!thread->HasPendingException()) {
job::MicroJobQueue::ExecutePendingJob(thread, thread->GetCurrentEcmaContext()->GetMicroJobQueue());
}
// Return capability.[[Promise]].
return capability->GetPromise();
}
int SourceTextModule::EvaluateForConcurrent(JSThread *thread, const JSHandle<SourceTextModule> &module,
@ -991,51 +1046,47 @@ int SourceTextModule::EvaluateForConcurrent(JSThread *thread, const JSHandle<Sou
// 2. Assert: module.[[Status]] is "instantiated" or "evaluated".
[[maybe_unused]] ModuleStatus status = module->GetStatus();
ASSERT((status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATED));
// 3. Let stack be a new empty List.
CVector<JSHandle<SourceTextModule>> stack;
// 4. Let result be InnerModuleEvaluation(module, stack, 0)
JSHandle<ModuleRecord> moduleRecord = JSHandle<ModuleRecord>::Cast(module);
int result = SourceTextModule::ModuleEvaluation(thread, moduleRecord, stack, 0, method);
int result = SourceTextModule::ModuleEvaluation(thread, moduleRecord, 0, method);
// 5. If result is an abrupt completion, then
if (thread->HasPendingException()) {
// a. For each module m in stack, do
for (auto mm : stack) {
// i. Assert: m.[[Status]] is "evaluating".
ASSERT(mm->GetStatus() == ModuleStatus::EVALUATING);
// ii. Set m.[[Status]] to "evaluated".
mm->SetStatus(ModuleStatus::EVALUATED);
// iii. Set m.[[EvaluationError]] to result.
mm->SetEvaluationError(result);
}
// b. Assert: module.[[EvaluationError]] is result.
ASSERT(module->GetEvaluationError() == result);
// c. return result
return result;
} else {
job::MicroJobQueue::ExecutePendingJob(thread, thread->GetCurrentEcmaContext()->GetMicroJobQueue());
return SourceTextModule::UNDEFINED_INDEX;
}
// 6. Assert: module.[[EvaluationError]] is undefined.
ASSERT(module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
// 7. Assert: stack is empty.
ASSERT(stack.empty());
// 8. Return undefined.
return SourceTextModule::UNDEFINED_INDEX;
}
int SourceTextModule::InnerModuleEvaluation(JSThread *thread, const JSHandle<ModuleRecord> &moduleRecord,
CVector<JSHandle<SourceTextModule>> &stack, int index,
const void *buffer, size_t size, bool excuteFromJob)
{
// 1. If module is not a Source Text Module Record, then
// 1.If module is not a Cyclic Module Record, then
if (!moduleRecord.GetTaggedValue().IsSourceTextModule()) {
// a. Perform ? module.Instantiate().
ModuleRecord::Instantiate(thread, JSHandle<JSTaggedValue>::Cast(moduleRecord));
// a. Let promise be ! module.Evaluate().
JSTaggedValue promise = ModuleRecord::Evaluate(thread, JSHandle<JSTaggedValue>::Cast(moduleRecord));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
// b. Return index.
// b. Assert: promise.[[PromiseState]] is not PENDING.
PromiseState state = JSPromise::Cast(promise.GetTaggedObject())->GetPromiseState();
ASSERT(state != PromiseState::PENDING);
// c. If promise.[[PromiseState]] is REJECTED, then
// i. Return ThrowCompletion(promise.[[PromiseResult]]).
if (state == PromiseState::REJECTED) {
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue promiseResult = JSPromise::Cast(promise.GetTaggedObject())->GetPromiseResult();
JSHandle<JSObject> error =
factory->GetJSError(base::ErrorType::ERROR, nullptr);
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), promiseResult.GetInt());
}
// d. Return index.
return index;
}
JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
// 2.If module.[[Status]] is "evaluated", then
// 2. If module.[[Status]] is either EVALUATING-ASYNC or EVALUATED, then
ModuleStatus status = module->GetStatus();
if (status == ModuleStatus::EVALUATED) {
if (status == ModuleStatus::EVALUATING_ASYNC || status == ModuleStatus::EVALUATED) {
// a. If module.[[EvaluationError]] is undefined, return index
if (module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX) {
return index;
@ -1055,11 +1106,13 @@ int SourceTextModule::InnerModuleEvaluation(JSThread *thread, const JSHandle<Mod
module->SetDFSIndex(index);
// 7. Set module.[[DFSAncestorIndex]] to index.
module->SetDFSAncestorIndex(index);
// 8. Set index to index + 1.
// 8. Set module.[[PendingAsyncDependencies]] to 0.
module->SetPendingAsyncDependencies(0);
// 9. Set index to index + 1.
index++;
// 9. Append module to stack.
// 10. Append module to stack.
stack.emplace_back(module);
// 10. For each String required that is an element of module.[[RequestedModules]], do
// 11. For each String required that is an element of module.[[RequestedModules]], do
if (!module->GetRequestedModules().IsUndefined()) {
JSHandle<TaggedArray> requestedModules(thread, module->GetRequestedModules());
size_t requestedModulesLen = requestedModules->GetLength();
@ -1092,25 +1145,48 @@ int SourceTextModule::InnerModuleEvaluation(JSThread *thread, const JSHandle<Mod
requiredModule->SetStatus(ModuleStatus::EVALUATED);
continue;
}
// c. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
// b. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
JSHandle<ModuleRecord> requiredModuleRecord = JSHandle<ModuleRecord>::Cast(requiredModule);
index = SourceTextModule::InnerModuleEvaluation(thread, requiredModuleRecord, stack, index);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
// d. Assert: requiredModule.[[Status]] is either "evaluating" or "evaluated".
// c. If requiredModule is a Cyclic Module Record, then
// i. Assert: requiredModule.[[Status]] is one of EVALUATING, EVALUATING-ASYNC, or EVALUATED.
ModuleStatus requiredModuleStatus = requiredModule->GetStatus();
ASSERT((requiredModuleStatus == ModuleStatus::EVALUATING ||
requiredModuleStatus == ModuleStatus::EVALUATED));
// e. Assert: requiredModule.[[Status]] is "evaluating" if and only if requiredModule is in stack.
ASSERT(requiredModuleStatus == ModuleStatus::EVALUATING ||
requiredModuleStatus == ModuleStatus::EVALUATING_ASYNC ||
requiredModuleStatus == ModuleStatus::EVALUATED);
// ii. Assert: requiredModule.[[Status]] is EVALUATING if and only if stack contains requiredModule.
if (requiredModuleStatus == ModuleStatus::EVALUATING) {
ASSERT(std::find(stack.begin(), stack.end(), requiredModule) != stack.end());
}
// f. If requiredModule.[[Status]] is "evaluating", then
if (std::find(stack.begin(), stack.end(), requiredModule) != stack.end()) {
ASSERT(requiredModuleStatus == ModuleStatus::EVALUATING);
}
// iii. If requiredModule.[[Status]] is EVALUATING, then
if (requiredModuleStatus == ModuleStatus::EVALUATING) {
// i. Assert: requiredModule is a Source Text Module Record.
// ii. Set module.[[DFSAncestorIndex]] to min(
// module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
// 1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]],
// requiredModule.[[DFSAncestorIndex]]).
int dfsAncIdx = std::min(module->GetDFSAncestorIndex(), requiredModule->GetDFSAncestorIndex());
module->SetDFSAncestorIndex(dfsAncIdx);
// iv. Else,
} else {
// 1. Set requiredModule to requiredModule.[[CycleRoot]].
requiredModule.Update(requiredModule->GetCycleRoot());
// 2. Assert: requiredModule.[[Status]] is either EVALUATING-ASYNC or EVALUATED.
requiredModuleStatus = requiredModule->GetStatus();
ASSERT(requiredModuleStatus == ModuleStatus::EVALUATING_ASYNC ||
requiredModuleStatus == ModuleStatus::EVALUATED);
// 3. If requiredModule.[[EvaluationError]] is not EMPTY, return ? requiredModule.[[EvaluationError]].
if (requiredModule->GetEvaluationError() != SourceTextModule::UNDEFINED_INDEX) {
return requiredModule->GetEvaluationError();
}
}
// v. If requiredModule.[[AsyncEvaluation]] is true, then
// 1. Set module.[[PendingAsyncDependencies]] to module.[[PendingAsyncDependencies]] + 1.
// 2. Append module to requiredModule.[[AsyncParentModules]].
if (requiredModule->IsAsyncEvaluating()) {
module->SetPendingAsyncDependencies(module->GetPendingAsyncDependencies() + 1);
AddAsyncParentModule(thread, requiredModule, module);
}
// if requiredModule is CommonJS Module, instantiate here (after CommonJS execution).
if (moduleType == ModuleTypes::CJS_MODULE) {
@ -1118,16 +1194,31 @@ int SourceTextModule::InnerModuleEvaluation(JSThread *thread, const JSHandle<Mod
}
}
}
// 11. Perform ? ModuleExecution(module).
SourceTextModule::ModuleExecution(thread, module, buffer, size, excuteFromJob);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
// 12. Assert: module occurs exactly once in stack.
// 13. Assert: module.[[DFSAncestorIndex]] is less than or equal to module.[[DFSIndex]].
int pendingAsyncDependencies = module->GetPendingAsyncDependencies();
bool hasTLA = module->GetHasTLA();
auto moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
// 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.
module->SetAsyncEvaluatingOrdinal(moduleManager->NextModuleAsyncEvaluatingOrdinal());
// d. If module.[[PendingAsyncDependencies]] = 0, perform ExecuteAsyncModule(module).
if (pendingAsyncDependencies == 0) {
SourceTextModule::ExecuteAsyncModule(thread, module, buffer, size, excuteFromJob);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
}
} else {
// 13. Else, Perform ? module.ExecuteModule().
SourceTextModule::ModuleExecution(thread, module, buffer, size, excuteFromJob);
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);
// 14. If module.[[DFSAncestorIndex]] equals module.[[DFSIndex]], then
// 16. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
if (dfsAncIdx == dfsIdx) {
// a. Let done be false.
bool done = false;
@ -1137,20 +1228,62 @@ int SourceTextModule::InnerModuleEvaluation(JSThread *thread, const JSHandle<Mod
JSHandle<SourceTextModule> requiredModule = stack.back();
// ii. Remove the last element of stack.
stack.pop_back();
// iii. Set requiredModule.[[Status]] to "evaluated".
requiredModule->SetStatus(ModuleStatus::EVALUATED);
// iv. If requiredModule and module are the same Module Record, set done to true.
// 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(module.GetTaggedValue(), requiredModule.GetTaggedValue())) {
done = true;
}
// vii. Set requiredModule.[[CycleRoot]] to module.
requiredModule->SetCycleRoot(thread, module);
}
}
return index;
}
void SourceTextModule::HandleConcurrentEvaluateResult(JSThread *thread, JSHandle<SourceTextModule> &module,
const CVector<JSHandle<SourceTextModule>> &stack, int result)
{
ModuleStatus status;
// 9. If result is an abrupt completion, then
if (thread->HasPendingException()) {
// a. For each module m in stack, do
for (auto mm : stack) {
// i. Assert: m.[[Status]] is "evaluating".
ASSERT(mm->GetStatus() == ModuleStatus::EVALUATING);
// ii. Set m.[[Status]] to "evaluated".
mm->SetStatus(ModuleStatus::EVALUATED);
// iii. Set m.[[EvaluationError]] to result.
mm->SetEvaluationError(result);
}
// b. Assert: module.[[Status]] is "evaluated" and module.[[EvaluationError]] is result.
status = module->GetStatus();
ASSERT(status == ModuleStatus::EVALUATED && module->GetEvaluationError() == result);
// 10. Else,
} else {
// 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(module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
// c. If module.[[AsyncEvaluation]] is false, then
// i. Assert: module.[[Status]] is EVALUATED.
if (!module->IsAsyncEvaluating()) {
ASSERT(status == ModuleStatus::EVALUATED);
}
// d. Assert: stack is empty.
ASSERT(stack.empty());
}
}
int SourceTextModule::ModuleEvaluation(JSThread *thread, const JSHandle<ModuleRecord> &moduleRecord,
CVector<JSHandle<SourceTextModule>> &stack, int index,
const JSHandle<Method> &method)
int index, const JSHandle<Method> &method)
{
JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
if (!module->GetRequestedModules().IsUndefined()) {
@ -1184,7 +1317,10 @@ int SourceTextModule::ModuleEvaluation(JSThread *thread, const JSHandle<ModuleRe
continue;
}
JSHandle<ModuleRecord> requiredModuleRecord = JSHandle<ModuleRecord>::Cast(requiredModule);
index = SourceTextModule::InnerModuleEvaluation(thread, requiredModuleRecord, stack, index);
CVector<JSHandle<SourceTextModule>> stack;
int result = SourceTextModule::InnerModuleEvaluation(thread, requiredModuleRecord, stack, 0);
index += result;
HandleConcurrentEvaluateResult(thread, requiredModule, stack, result);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index);
[[maybe_unused]] ModuleStatus requiredModuleStatus = requiredModule->GetStatus();
ASSERT(requiredModuleStatus == ModuleStatus::EVALUATED);
@ -1196,8 +1332,8 @@ int SourceTextModule::ModuleEvaluation(JSThread *thread, const JSHandle<ModuleRe
return index;
}
void SourceTextModule::ModuleExecution(JSThread *thread, const JSHandle<SourceTextModule> &module,
const void *buffer, size_t size, bool excuteFromJob)
Expected<JSTaggedValue, bool> SourceTextModule::ModuleExecution(JSThread *thread,
const JSHandle<SourceTextModule> &module, const void *buffer, size_t size, bool excuteFromJob)
{
JSTaggedValue moduleFileName = module->GetEcmaModuleFilename();
ASSERT(moduleFileName.IsString());
@ -1224,9 +1360,9 @@ void SourceTextModule::ModuleExecution(JSThread *thread, const JSHandle<SourceTe
if (jsPandaFile == nullptr) {
CString msg = "Load file with filename '" + moduleFilenameStr + "' failed, recordName '" +
entryPoint.c_str() + "'";
THROW_ERROR(thread, ErrorType::REFERENCE_ERROR, msg.c_str());
THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
}
JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entryPoint, excuteFromJob);
return JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entryPoint, excuteFromJob);
}
void SourceTextModule::AddImportEntry(JSThread *thread, const JSHandle<SourceTextModule> &module,
@ -1694,4 +1830,304 @@ 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<SourceTextModule> &module,
JSHandle<SourceTextModule> &parent)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue asyncParentModules = module->GetAsyncParentModules();
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);
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, bool excuteFromJob)
{
// 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());
JSTaggedValue moduleFileName = module->GetEcmaModuleFilename();
ASSERT(moduleFileName.IsString());
CString moduleFilenameStr = ConvertToString(EcmaString::Cast(moduleFileName.GetTaggedObject()));
std::string entryPoint;
JSTaggedValue moduleRecordName = module->GetEcmaModuleRecordName();
if (moduleRecordName.IsUndefined()) {
entryPoint = JSPandaFile::ENTRY_FUNCTION_NAME;
} else {
ASSERT(moduleRecordName.IsString());
entryPoint = ConvertToString(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);
}
if (jsPandaFile == nullptr) {
CString msg = "Load file with filename '" + moduleFilenameStr + "' failed, recordName '" +
entryPoint.c_str() + "'";
THROW_ERROR(thread, ErrorType::REFERENCE_ERROR, msg.c_str());
}
Expected<JSTaggedValue, bool> result =
JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entryPoint, excuteFromJob);
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<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);
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);
}
void SourceTextModule::GatherAvailableAncestors(JSThread *thread, const JSHandle<SourceTextModule> &module,
AsyncParentCompletionSet &execList)
{
auto globalConstants = thread->GlobalConstants();
JSTaggedValue asyncParentModulesValue = module->GetAsyncParentModules();
if (asyncParentModulesValue.IsUndefined()) {
return;
}
JSMutableHandle<SourceTextModule> cycleRoot(thread, globalConstants->GetUndefined());
JSHandle<TaggedArray> 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<SourceTextModule> parentModule(thread, asyncParentModules->Get(idx));
// a. If execList does not contain m and m.[[CycleRoot]].[[EvaluationError]] is EMPTY, then
cycleRoot.Update(parentModule->GetCycleRoot());
if (execList.find(parentModule) == execList.end() &&
cycleRoot->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX) {
// i. Assert: m.[[Status]] is EVALUATING-ASYNC.
ASSERT(parentModule->GetStatus() == ModuleStatus::EVALUATING_ASYNC);
// ii. Assert: m.[[EvaluationError]] is EMPTY.
ASSERT(parentModule->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
// 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<SourceTextModule> &module)
{
// 1. If module.[[Status]] is EVALUATED, then
// a. Assert: module.[[EvaluationError]] is not EMPTY.
// b. Return UNUSED.
if (module->GetStatus() == ModuleStatus::EVALUATED) {
ASSERT(module->GetEvaluationError() != SourceTextModule::UNDEFINED_INDEX);
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->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
// 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();
if (!topLevelCapabilityValue.IsUndefined()) {
ASSERT(JSTaggedValue::SameValue(module->GetCycleRoot(), module.GetTaggedValue()));
JSHandle<PromiseCapability> topLevelCapability(thread, topLevelCapabilityValue);
JSHandle<JSTaggedValue> resolve(thread, topLevelCapability->GetResolve());
JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1);
RETURN_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(JSTaggedValue::Undefined());
[[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
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<SourceTextModule> m : execList) {
// a. If m.[[Status]] is EVALUATED, then
// i. Assert: m.[[EvaluationError]] is not EMPTY.
if (m->GetStatus() == ModuleStatus::EVALUATED) {
ASSERT(m->GetEvaluationError() != UNDEFINED_INDEX);
// 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<JSTaggedValue, bool> 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();
if (!capabilityValue.IsUndefined()) {
ASSERT(JSTaggedValue::SameValue(m->GetCycleRoot(), m.GetTaggedValue()));
JSHandle<PromiseCapability> topLevelCapability(thread, capabilityValue);
JSHandle<JSTaggedValue> resolve(thread, topLevelCapability->GetResolve());
JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1);
RETURN_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(JSTaggedValue::Undefined());
[[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
RETURN_IF_ABRUPT_COMPLETION(thread);
}
}
}
}
}
void SourceTextModule::AsyncModuleExecutionRejected(JSThread *thread, const JSHandle<SourceTextModule> &module,
JSTaggedValue error)
{
// 1. If module.[[Status]] is EVALUATED, then
// a. Assert: module.[[EvaluationError]] is not EMPTY.
// b. Return UNUSED.
if (module->GetStatus() == ModuleStatus::EVALUATED) {
ASSERT(module->GetEvaluationError() != SourceTextModule::UNDEFINED_INDEX);
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->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX);
// 5. Set module.[[EvaluationError]] to ThrowCompletion(error).
module->SetEvaluationError(MODULE_ERROR);
// 6. Set module.[[Status]] to EVALUATED.
module->SetStatus(ModuleStatus::EVALUATED);
// 7. For each Cyclic Module Record m of module.[[AsyncParentModules]], do
// a. Perform AsyncModuleExecutionRejected(m, error).
auto globalConstants = thread->GlobalConstants();
JSTaggedValue asyncParentModulesValue = module->GetAsyncParentModules();
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(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();
if (!topLevelCapabilityValue.IsUndefined()) {
JSHandle<JSTaggedValue> exceptionHandle(thread, error);
// if caught exceptionHandle type is JSError
if (exceptionHandle->IsJSError()) {
thread->GetCurrentEcmaContext()->HandleUncaughtException(error);
}
ASSERT(JSTaggedValue::SameValue(module->GetCycleRoot(), module.GetTaggedValue()));
JSHandle<PromiseCapability> topLevelCapability(thread, topLevelCapabilityValue);
JSHandle<JSTaggedValue> reject(thread, topLevelCapability->GetReject());
JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
RETURN_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(error);
[[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
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());
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<JSAsyncModuleRejectedFunction> rejectedFunc =
JSHandle<JSAsyncModuleRejectedFunction>::Cast(base::BuiltinsBase::GetConstructor(argv));
JSHandle<SourceTextModule> module(thread, rejectedFunc->GetModule());
[[maybe_unused]] JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, 0);
AsyncModuleExecutionRejected(thread, module, value.GetTaggedValue());
return JSTaggedValue::Undefined();
}
} // namespace panda::ecmascript

View File

@ -23,7 +23,14 @@
#include "ecmascript/tagged_array.h"
namespace panda::ecmascript {
enum class ModuleStatus : uint8_t { UNINSTANTIATED = 0x01, INSTANTIATING, INSTANTIATED, EVALUATING, EVALUATED };
enum class ModuleStatus : uint8_t {
UNINSTANTIATED = 0x01,
INSTANTIATING,
INSTANTIATED,
EVALUATING,
EVALUATING_ASYNC,
EVALUATED
};
enum class ModuleTypes : uint8_t {
ECMA_MODULE = 0x01,
@ -45,9 +52,21 @@ enum class LoadingTypes : uint8_t {
class SourceTextModule final : public ModuleRecord {
public:
static constexpr int UNDEFINED_INDEX = -1;
static constexpr int MODULE_ERROR = 1;
static constexpr size_t DEFAULT_DICTIONART_CAPACITY = 2;
static constexpr size_t DEFAULT_ARRAY_CAPACITY = 2;
static constexpr uint8_t DEREGISTER_MODULE_TAG = 1;
static constexpr uint32_t FIRST_ASYNC_EVALUATING_ORDINAL = 2;
static constexpr uint32_t NOT_ASYNC_EVALUATED = 0;
static constexpr uint32_t ASYNC_EVALUATE_DID_FINISH = 1;
struct AsyncEvaluatingOrdinalCompare {
bool operator()(const JSHandle<SourceTextModule> &lhs, const JSHandle<SourceTextModule> &rhs) const
{
return lhs->GetAsyncEvaluatingOrdinal() < rhs->GetAsyncEvaluatingOrdinal();
}
};
using AsyncParentCompletionSet =
CSet<JSHandle<SourceTextModule>, AsyncEvaluatingOrdinalCompare>;
CAST_CHECK(SourceTextModule, IsSourceTextModule);
@ -85,9 +104,28 @@ public:
size_t size = 0, bool excuteFromJob = false);
// 15.2.1.16.5.2 ModuleExecution ( module )
static void ModuleExecution(JSThread *thread, const JSHandle<SourceTextModule> &module,
const void *buffer = nullptr, size_t size = 0, bool excuteFromJob = false);
static Expected<JSTaggedValue, bool> ModuleExecution(JSThread *thread, const JSHandle<SourceTextModule> &module,
const void *buffer = nullptr, size_t size = 0, bool excuteFromJob = false);
// 16.2.1.5.3.2 ExecuteAsyncModule ( module )
static void ExecuteAsyncModule(JSThread *thread, const JSHandle<SourceTextModule> &module,
const void *buffer = nullptr, size_t size = 0, bool excuteFromJob = false);
// 16.2.1.5.3.3 GatherAvailableAncestors ( module, execList )
static void GatherAvailableAncestors(JSThread *thread, const JSHandle<SourceTextModule> &module,
AsyncParentCompletionSet &execList);
// 16.2.1.5.3.4 AsyncModuleExecutionFulfilled ( module )
static void AsyncModuleExecutionFulfilled(JSThread *thread, const JSHandle<SourceTextModule> &module);
// 16.2.1.5.3.5 AsyncModuleExecutionRejected ( module, error )
static void AsyncModuleExecutionRejected(JSThread *thread, const JSHandle<SourceTextModule> &module,
JSTaggedValue error);
static JSTaggedValue AsyncModuleFulfilledFunc(EcmaRuntimeCallInfo *argv);
static JSTaggedValue AsyncModuleRejectedFunc(EcmaRuntimeCallInfo *argv);
static void AddAsyncParentModule(JSThread *thread, JSHandle<SourceTextModule> &module,
JSHandle<SourceTextModule> &parent);
// 15.2.1.18 Runtime Semantics: GetModuleNamespace ( module )
static JSHandle<JSTaggedValue> GetModuleNamespace(JSThread *thread, const JSHandle<SourceTextModule> &module);
@ -124,10 +162,15 @@ public:
ACCESSORS(LocalExportEntries, LOCAL_EXPORT_ENTTRIES_OFFSET, INDIRECT_EXPORT_ENTTRIES_OFFSET);
ACCESSORS(IndirectExportEntries, INDIRECT_EXPORT_ENTTRIES_OFFSET, START_EXPORT_ENTTRIES_OFFSET);
ACCESSORS(StarExportEntries, START_EXPORT_ENTTRIES_OFFSET, NAME_DICTIONARY_OFFSET);
ACCESSORS(NameDictionary, NAME_DICTIONARY_OFFSET, EVALUATION_ERROR_OFFSET);
ACCESSORS(NameDictionary, NAME_DICTIONARY_OFFSET, CYCLE_ROOT_OFFSET);
ACCESSORS(CycleRoot, CYCLE_ROOT_OFFSET, TOP_LEVEL_CAPABILITY_OFFSET);
ACCESSORS(TopLevelCapability, TOP_LEVEL_CAPABILITY_OFFSET, ASYNC_PARENT_MODULES_OFFSET);
ACCESSORS(AsyncParentModules, ASYNC_PARENT_MODULES_OFFSET, EVALUATION_ERROR_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(EvaluationError, int32_t, EVALUATION_ERROR_OFFSET, DFS_ANCESTOR_INDEX_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(DFSAncestorIndex, int32_t, DFS_ANCESTOR_INDEX_OFFSET, DFS_INDEX_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(DFSIndex, int32_t, DFS_INDEX_OFFSET, BIT_FIELD_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(DFSIndex, int32_t, DFS_INDEX_OFFSET, ASYNC_EVALUATION_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(AsyncEvaluatingOrdinal, uint32_t, ASYNC_EVALUATION_OFFSET, PENDING_DEPENDENCIES_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(PendingAsyncDependencies, int32_t, PENDING_DEPENDENCIES_OFFSET, BIT_FIELD_OFFSET);
ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
@ -136,21 +179,23 @@ public:
static constexpr size_t STATUS_BITS = 3;
static constexpr size_t MODULE_TYPE_BITS = 4;
static constexpr size_t IS_NEW_BC_VERSION_BITS = 1;
static constexpr size_t HASTLA_BITS = 1;
static constexpr size_t LOADING_TYPE_BITS = 3;
static constexpr uint16_t REGISTER_COUNTS = 16;
FIRST_BIT_FIELD(BitField, Status, ModuleStatus, STATUS_BITS)
NEXT_BIT_FIELD(BitField, Types, ModuleTypes, MODULE_TYPE_BITS, Status)
NEXT_BIT_FIELD(BitField, IsNewBcVersion, bool, IS_NEW_BC_VERSION_BITS, Types)
NEXT_BIT_FIELD(BitField, LoadingTypes, LoadingTypes, LOADING_TYPE_BITS, IsNewBcVersion)
NEXT_BIT_FIELD(BitField, HasTLA, bool, HASTLA_BITS, IsNewBcVersion)
NEXT_BIT_FIELD(BitField, LoadingTypes, LoadingTypes, LOADING_TYPE_BITS, HasTLA)
NEXT_BIT_FIELD(BitField, RegisterCounts, uint16_t, REGISTER_COUNTS, LoadingTypes)
DECL_DUMP()
DECL_VISIT_OBJECT(SOURCE_TEXT_MODULE_OFFSET, EVALUATION_ERROR_OFFSET)
// 15.2.1.16.5 Evaluate()
static int Evaluate(JSThread *thread, const JSHandle<SourceTextModule> &module,
const void *buffer = nullptr, size_t size = 0, bool excuteFromJob = false);
static JSTaggedValue Evaluate(JSThread *thread, const JSHandle<SourceTextModule> &module,
const void *buffer = nullptr, size_t size = 0, bool excuteFromJob = false);
// 15.2.1.16.4 Instantiate()
static int Instantiate(JSThread *thread, const JSHandle<JSTaggedValue> &moduleHdl,
@ -186,8 +231,7 @@ public:
static int EvaluateForConcurrent(JSThread *thread, const JSHandle<SourceTextModule> &module,
const JSHandle<Method> &method);
static int ModuleEvaluation(JSThread *thread, const JSHandle<ModuleRecord> &moduleRecord,
CVector<JSHandle<SourceTextModule>> &stack, int index,
const JSHandle<Method> &method);
int index, const JSHandle<Method> &method);
private:
static void SetExportName(JSThread *thread,
@ -228,6 +272,12 @@ private:
int &index, bool excuteFromJob);
static int HandleInstantiateException(JSHandle<SourceTextModule> &module,
const CVector<JSHandle<SourceTextModule>> &stack, int result);
static void HandleEvaluateResult(JSThread *thread, JSHandle<SourceTextModule> &module,
JSHandle<PromiseCapability> &capability,
const CVector<JSHandle<SourceTextModule>> &stack, int result);
static void HandleConcurrentEvaluateResult(JSThread *thread, JSHandle<SourceTextModule> &module,
const CVector<JSHandle<SourceTextModule>> &stack, int result);
bool IsAsyncEvaluating();
};
class ResolvedBinding final : public Record {

View File

@ -48,6 +48,9 @@ JSHandle<JSTaggedValue> ModuleDataExtractor::ParseModule(JSThread *thread, const
JSHandle<SourceTextModule> moduleRecord = factory->NewSourceTextModule();
ModuleDataExtractor::ExtractModuleDatas(thread, jsPandaFile, moduleId, moduleRecord);
bool hasTLA = jsPandaFile->GetHasTopLevelAwait(descriptor);
moduleRecord->SetHasTLA(hasTLA);
JSHandle<EcmaString> ecmaModuleFilename = factory->NewFromUtf8(moduleFilename);
moduleRecord->SetEcmaModuleFilename(thread, ecmaModuleFilename);

View File

@ -279,10 +279,10 @@ HWTEST_F_L0(EcmaModuleTest, Instantiate_Evaluate_GetNamespace_SetNamespace)
JSHandle<SourceTextModule> module = moduleManager->HostGetImportedModule("module_test_module_test_C");
module->SetStatus(ModuleStatus::UNINSTANTIATED);
ModuleRecord::Instantiate(thread, JSHandle<JSTaggedValue>(module));
int res = ModuleRecord::Evaluate(thread, JSHandle<JSTaggedValue>(module));
JSTaggedValue res = ModuleRecord::Evaluate(thread, JSHandle<JSTaggedValue>(module));
ModuleRecord::GetNamespace(module.GetTaggedValue());
ModuleRecord::SetNamespace(thread, module.GetTaggedValue(), JSTaggedValue::Undefined());
EXPECT_TRUE(res == SourceTextModule::UNDEFINED_INDEX);
EXPECT_TRUE(res.IsJSPromise());
}
HWTEST_F_L0(EcmaModuleTest, ConcatFileNameWithMerge1)

View File

@ -1294,6 +1294,14 @@ void ObjectFactory::InitializeJSObject(const JSHandle<JSObject> &obj, const JSHa
JSFunction::InitializeJSFunction(thread_, JSHandle<JSFunction>(obj));
JSPromiseExecutorFunction::Cast(*obj)->SetCapability(thread_, JSTaggedValue::Undefined());
break;
case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION:
JSFunction::InitializeJSFunction(thread_, JSHandle<JSFunction>(obj));
JSAsyncModuleFulfilledFunction::Cast(*obj)->SetModule(thread_, JSTaggedValue::Undefined());
break;
case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION:
JSFunction::InitializeJSFunction(thread_, JSHandle<JSFunction>(obj));
JSAsyncModuleRejectedFunction::Cast(*obj)->SetModule(thread_, JSTaggedValue::Undefined());
break;
case JSType::JS_ASYNC_GENERATOR_RESUME_NEXT_RETURN_PROCESSOR_RST_FTN:
JSFunction::InitializeJSFunction(thread_, JSHandle<JSFunction>(obj));
JSAsyncGeneratorResNextRetProRstFtn::Cast(*obj)->SetAsyncGeneratorObject(thread_,
@ -2982,6 +2990,36 @@ JSHandle<JSPromiseExecutorFunction> ObjectFactory::CreateJSPromiseExecutorFuncti
return executorFunction;
}
JSHandle<JSAsyncModuleFulfilledFunction> ObjectFactory::CreateJSAsyncModuleFulfilledFunction()
{
JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetAsyncModuleFulfilledFunctionClass());
JSHandle<JSAsyncModuleFulfilledFunction> fulfilledFunction =
JSHandle<JSAsyncModuleFulfilledFunction>::Cast(NewJSObject(hclass));
fulfilledFunction->SetModule(thread_, JSTaggedValue::Undefined());
JSHandle<JSFunction> function = JSHandle<JSFunction>::Cast(fulfilledFunction);
JSFunction::InitializeJSFunction(thread_, function);
fulfilledFunction->SetMethod(
thread_, vm_->GetMethodByIndex(MethodIndex::BUILTINS_ASYNC_MODULE_FULFILLED_FUNCTION));
JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(FunctionLength::ONE));
return fulfilledFunction;
}
JSHandle<JSAsyncModuleRejectedFunction> ObjectFactory::CreateJSAsyncModuleRejectedFunction()
{
JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetAsyncModuleRejectedFunctionClass());
JSHandle<JSAsyncModuleRejectedFunction> rejectedFunction =
JSHandle<JSAsyncModuleRejectedFunction>::Cast(NewJSObject(hclass));
rejectedFunction->SetModule(thread_, JSTaggedValue::Undefined());
JSHandle<JSFunction> function = JSHandle<JSFunction>::Cast(rejectedFunction);
JSFunction::InitializeJSFunction(thread_, function);
rejectedFunction->SetMethod(
thread_, vm_->GetMethodByIndex(MethodIndex::BUILTINS_ASYNC_MODULE_REJECTED_FUNCTION));
JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(FunctionLength::ONE));
return rejectedFunction;
}
JSHandle<JSPromiseAllResolveElementFunction> ObjectFactory::NewJSPromiseAllResolveElementFunction()
{
JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
@ -4066,6 +4104,12 @@ JSHandle<SourceTextModule> ObjectFactory::NewSourceTextModule()
obj->SetIndirectExportEntries(thread_, undefinedValue);
obj->SetStarExportEntries(thread_, undefinedValue);
obj->SetNameDictionary(thread_, undefinedValue);
obj->SetCycleRoot(thread_, undefinedValue);
obj->SetTopLevelCapability(thread_, undefinedValue);
obj->SetAsyncParentModules(thread_, undefinedValue);
obj->SetHasTLA(false);
obj->SetAsyncEvaluatingOrdinal(SourceTextModule::NOT_ASYNC_EVALUATED);
obj->SetPendingAsyncDependencies(SourceTextModule::UNDEFINED_INDEX);
obj->SetDFSIndex(SourceTextModule::UNDEFINED_INDEX);
obj->SetDFSAncestorIndex(SourceTextModule::UNDEFINED_INDEX);
obj->SetEvaluationError(SourceTextModule::UNDEFINED_INDEX);

View File

@ -71,6 +71,8 @@ class JSDataView;
class JSPromise;
class JSPromiseReactionsFunction;
class JSPromiseExecutorFunction;
class JSAsyncModuleFulfilledFunction;
class JSAsyncModuleRejectedFunction;
class JSPromiseAllResolveElementFunction;
class JSAsyncGeneratorResNextRetProRstFtn;
class JSPromiseAnyRejectElementFunction;
@ -422,6 +424,10 @@ public:
JSHandle<JSPromiseExecutorFunction> CreateJSPromiseExecutorFunction();
JSHandle<JSAsyncModuleFulfilledFunction> CreateJSAsyncModuleFulfilledFunction();
JSHandle<JSAsyncModuleRejectedFunction> CreateJSAsyncModuleRejectedFunction();
JSHandle<JSPromiseAllResolveElementFunction> NewJSPromiseAllResolveElementFunction();
JSHandle<JSPromiseAnyRejectElementFunction> NewJSPromiseAnyRejectElementFunction();

View File

@ -487,6 +487,22 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump)
DUMP_FOR_HANDLE(promiseExeFunc);
break;
}
case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION: {
CHECK_DUMP_FIELDS(JSFunction::SIZE, JSAsyncModuleFulfilledFunction::SIZE, 1U);
JSHandle<JSHClass> moduleFulfilledClass =
JSHandle<JSHClass>::Cast(globalEnv->GetAsyncModuleFulfilledFunctionClass());
JSHandle<JSObject> moduleFulfilledFunc = factory->NewJSObjectWithInit(moduleFulfilledClass);
DUMP_FOR_HANDLE(moduleFulfilledFunc);
break;
}
case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION: {
CHECK_DUMP_FIELDS(JSFunction::SIZE, JSAsyncModuleRejectedFunction::SIZE, 1U);
JSHandle<JSHClass> moduleRejectedClass =
JSHandle<JSHClass>::Cast(globalEnv->GetAsyncModuleRejectedFunctionClass());
JSHandle<JSObject> moduleRejectedFunc = factory->NewJSObjectWithInit(moduleRejectedClass);
DUMP_FOR_HANDLE(moduleRejectedFunc);
break;
}
case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: {
CHECK_DUMP_FIELDS(JSFunction::SIZE, JSPromiseAllResolveElementFunction::SIZE, 5U);
JSHandle<JSHClass> promiseAllClass =
@ -1289,7 +1305,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump)
break;
}
case JSType::SOURCE_TEXT_MODULE_RECORD: {
CHECK_DUMP_FIELDS(ModuleRecord::SIZE, SourceTextModule::SIZE, 12U);
CHECK_DUMP_FIELDS(ModuleRecord::SIZE, SourceTextModule::SIZE, 16U);
JSHandle<SourceTextModule> moduleSourceRecord = factory->NewSourceTextModule();
DUMP_FOR_HANDLE(moduleSourceRecord);
break;