From b21f1eae7ca52ff9a906d7f80f1b97aad691ed08 Mon Sep 17 00:00:00 2001 From: lichenshuai Date: Sat, 9 Dec 2023 22:26:03 +0800 Subject: [PATCH] support top-level await for module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持ECMA2022规范模块顶层await Issue: #I8LMRX Signed-off-by: lichenshuai Change-Id: Id09ac6f6bc6df9d4790d33f998b2b87586650658 --- ecmascript/builtins/builtins.cpp | 12 + ecmascript/builtins/builtins_method_index.h | 2 + ecmascript/dfx/hprof/heap_snapshot.cpp | 4 + ecmascript/dump.cpp | 66 ++ ecmascript/ecma_context.cpp | 6 +- ecmascript/ecma_vm.cpp | 4 +- ecmascript/ecma_vm.h | 1 + ecmascript/global_env_fields.h | 2 + ecmascript/js_function.h | 24 + ecmascript/js_hclass.h | 12 + ecmascript/js_tagged_value-inl.h | 10 + ecmascript/js_tagged_value.h | 2 + ecmascript/jspandafile/js_pandafile.cpp | 5 + ecmascript/jspandafile/js_pandafile.h | 15 + .../tests/js_pandafile_executor_test.cpp | 3 - ecmascript/mem/object_xray.h | 10 + ecmascript/module/js_module_manager.h | 8 + ecmascript/module/js_module_record.cpp | 2 +- ecmascript/module/js_module_record.h | 2 +- ecmascript/module/js_module_source_text.cpp | 632 +++++++++++++++--- ecmascript/module/js_module_source_text.h | 72 +- ecmascript/module/module_data_extractor.cpp | 3 + ecmascript/module/tests/ecma_module_test.cpp | 4 +- ecmascript/object_factory.cpp | 44 ++ ecmascript/object_factory.h | 6 + ecmascript/tests/dump_test.cpp | 18 +- 26 files changed, 849 insertions(+), 120 deletions(-) diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index 6715ef019e..cd8ed02420 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -2343,6 +2343,18 @@ void Builtins::InitializeForPromiseFuncClass(const JSHandle &env) promiseExecutorFuncClass->SetExtensible(true); env->SetPromiseExecutorFunctionClass(thread_, promiseExecutorFuncClass); + JSHandle asyncModuleFulfilledFuncClass = factory_->NewEcmaHClass( + JSAsyncModuleFulfilledFunction::SIZE, JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION, env->GetFunctionPrototype()); + asyncModuleFulfilledFuncClass->SetCallable(true); + asyncModuleFulfilledFuncClass->SetExtensible(true); + env->SetAsyncModuleFulfilledFunctionClass(thread_, asyncModuleFulfilledFuncClass); + + JSHandle asyncModuleRejectedFuncClass = factory_->NewEcmaHClass( + JSAsyncModuleRejectedFunction::SIZE, JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION, env->GetFunctionPrototype()); + asyncModuleRejectedFuncClass->SetCallable(true); + asyncModuleRejectedFuncClass->SetExtensible(true); + env->SetAsyncModuleRejectedFunctionClass(thread_, asyncModuleRejectedFuncClass); + JSHandle promiseAllResolveElementFunctionClass = factory_->NewEcmaHClass(JSPromiseAllResolveElementFunction::SIZE, JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION, env->GetFunctionPrototype()); diff --git a/ecmascript/builtins/builtins_method_index.h b/ecmascript/builtins/builtins_method_index.h index 85dd043541..76c66e9a22 100644 --- a/ecmascript/builtins/builtins_method_index.h +++ b/ecmascript/builtins/builtins_method_index.h @@ -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 diff --git a/ecmascript/dfx/hprof/heap_snapshot.cpp b/ecmascript/dfx/hprof/heap_snapshot.cpp index 48caeaece0..786e2fefb5 100644 --- a/ecmascript/dfx/hprof/heap_snapshot.cpp +++ b/ecmascript/dfx/hprof/heap_snapshot.cpp @@ -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: diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index 0d3d5fe942..ff9a65f6fd 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -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 &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 &vec) con JSObject::DumpForSnapshot(vec); } +void JSAsyncModuleFulfilledFunction::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("module"), GetModule()); + JSObject::DumpForSnapshot(vec); +} + +void JSAsyncModuleRejectedFunction::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("module"), GetModule()); + JSObject::DumpForSnapshot(vec); +} + void JSPromiseAllResolveElementFunction::DumpForSnapshot(std::vector &vec) const { vec.emplace_back(CString("index"), GetIndex()); @@ -5501,6 +5561,12 @@ void SourceTextModule::DumpForSnapshot(std::vector &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 &vec) const diff --git a/ecmascript/ecma_context.cpp b/ecmascript/ecma_context.cpp index 7dc37fbfd1..a4ba2aa887 100644 --- a/ecmascript/ecma_context.cpp +++ b/ecmascript/ecma_context.cpp @@ -298,11 +298,13 @@ Expected EcmaContext::CommonInvokeEcmaEntrypoint(const JSPa EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle(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; diff --git a/ecmascript/ecma_vm.cpp b/ecmascript/ecma_vm.cpp index fc6b4d6c8b..b6f8806cef 100644 --- a/ecmascript/ecma_vm.cpp +++ b/ecmascript/ecma_vm.cpp @@ -584,7 +584,9 @@ void *EcmaVM::InternalMethodTable[] = { reinterpret_cast(builtins::BuiltinsPromiseHandler::throwerFunction), reinterpret_cast(JSAsyncGeneratorObject::ProcessorFulfilledFunc), reinterpret_cast(JSAsyncGeneratorObject::ProcessorRejectedFunc), - reinterpret_cast(JSAsyncFromSyncIterator::AsyncFromSyncIterUnwarpFunction) + reinterpret_cast(JSAsyncFromSyncIterator::AsyncFromSyncIterUnwarpFunction), + reinterpret_cast(SourceTextModule::AsyncModuleFulfilledFunc), + reinterpret_cast(SourceTextModule::AsyncModuleRejectedFunc) }; void EcmaVM::GenerateInternalNativeMethods() diff --git a/ecmascript/ecma_vm.h b/ecmascript/ecma_vm.h index 8425bed1db..13929514f9 100644 --- a/ecmascript/ecma_vm.h +++ b/ecmascript/ecma_vm.h @@ -76,6 +76,7 @@ template class JSHandle; class JSArrayBuffer; class JSFunction; +class SourceTextModule; class Program; class TSManager; class AOTFileManager; diff --git a/ecmascript/global_env_fields.h b/ecmascript/global_env_fields.h index 66f7480233..5510f0e620 100644 --- a/ecmascript/global_env_fields.h +++ b/ecmascript/global_env_fields.h @@ -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) \ diff --git a/ecmascript/js_function.h b/ecmascript/js_function.h index 74565f693a..2e973481b4 100644 --- a/ecmascript/js_function.h +++ b/ecmascript/js_function.h @@ -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); diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index a079a4c0b0..3d933dc99d 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -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; diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index b4e48709aa..fe9e00490a 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -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(); diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index 2ff8ad2f14..da836d983a 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -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; diff --git a/ecmascript/jspandafile/js_pandafile.cpp b/ecmascript/jspandafile/js_pandafile.cpp index 0486c3bd99..d6f652a06a 100644 --- a/ecmascript/jspandafile/js_pandafile.cpp +++ b/ecmascript/jspandafile/js_pandafile.cpp @@ -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().value(); } else if (std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) { info.moduleRecordIdx = fieldAccessor.GetValue().value(); + } else if (std::strcmp(HAS_TOP_LEVEL_AWAIT, fieldName) == 0) { + info.hasTopLevelAwait = fieldAccessor.GetValue().value(); } else if (std::strcmp(TYPE_FLAG, fieldName) == 0) { info.hasTSTypes = fieldAccessor.GetValue().value() != 0; } else if (std::strcmp(TYPE_SUMMARY_OFFSET, fieldName) == 0) { diff --git a/ecmascript/jspandafile/js_pandafile.h b/ecmascript/jspandafile/js_pandafile.h index fdfcc862d2..4436fa5246 100644 --- a/ecmascript/jspandafile/js_pandafile.h +++ b/ecmascript/jspandafile/js_pandafile.h @@ -37,6 +37,7 @@ public: int jsonStringId {-1}; CUnorderedSet vmListOfParsedConstPool; int moduleRecordIdx {-1}; + bool hasTopLevelAwait {false}; CUnorderedMap 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 GetClasses() const { return pf_->GetClasses(); diff --git a/ecmascript/jspandafile/tests/js_pandafile_executor_test.cpp b/ecmascript/jspandafile/tests/js_pandafile_executor_test.cpp index 2b1dae268d..408ddd5596 100644 --- a/ecmascript/jspandafile/tests/js_pandafile_executor_test.cpp +++ b/ecmascript/jspandafile/tests/js_pandafile_executor_test.cpp @@ -87,7 +87,6 @@ HWTEST_F_L0(JSPandaFileExecutorTest, Execute) Expected 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 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 foundPf = pfManager->FindJSPandaFile(fileName); @@ -161,7 +159,6 @@ HWTEST_F_L0(JSPandaFileExecutorTest, ExecuteFromBuffer) Expected 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 foundPf = pfManager->FindJSPandaFile(fileName); diff --git a/ecmascript/mem/object_xray.h b/ecmascript/mem/object_xray.h index 7684c992a7..116f74116c 100644 --- a/ecmascript/mem/object_xray.h +++ b/ecmascript/mem/object_xray.h @@ -183,6 +183,16 @@ public: jsPromiseExecutorFunction->VisitRangeSlot(visitor); break; } + case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION: { + auto jsAsyncModuleFulfilledFunction = JSAsyncModuleFulfilledFunction::Cast(object); + jsAsyncModuleFulfilledFunction->VisitRangeSlot(visitor); + break; + } + case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION: { + auto jsAsyncModuleRejectedFunction = JSAsyncModuleRejectedFunction::Cast(object); + jsAsyncModuleRejectedFunction->VisitRangeSlot(visitor); + break; + } case JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION: { auto jsAsyncFromSyncIterUnwarpFunction = JSAsyncFromSyncIterUnwarpFunction::Cast(object); jsAsyncFromSyncIterUnwarpFunction->VisitRangeSlot(visitor); diff --git a/ecmascript/module/js_module_manager.h b/ecmascript/module/js_module_manager.h index 8c7707bf7a..12abab3a99 100644 --- a/ecmascript/module/js_module_manager.h +++ b/ecmascript/module/js_module_manager.h @@ -90,6 +90,12 @@ public: static CString GetRecordName(JSTaggedValue module); static int GetExportObjectIndex(EcmaVM *vm, JSHandle 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()}; diff --git a/ecmascript/module/js_module_record.cpp b/ecmascript/module/js_module_record.cpp index 4d7a742cca..95c83b7d1e 100644 --- a/ecmascript/module/js_module_record.cpp +++ b/ecmascript/module/js_module_record.cpp @@ -27,7 +27,7 @@ int32_t ModuleRecord::Instantiate(JSThread *thread, const JSHandle &module) +JSTaggedValue ModuleRecord::Evaluate(JSThread *thread, const JSHandle &module) { if (module->IsSourceTextModule()) { JSHandle moduleRecord = JSHandle::Cast(module); diff --git a/ecmascript/module/js_module_record.h b/ecmascript/module/js_module_record.h index 0033d079a7..727085a70d 100644 --- a/ecmascript/module/js_module_record.h +++ b/ecmascript/module/js_module_record.h @@ -27,7 +27,7 @@ public: // 15.2.1.16.4 Instantiate() static int Instantiate(JSThread *thread, const JSHandle &module); // 15.2.1.16.5 Evaluate() - static int Evaluate(JSThread *thread, const JSHandle &module); + static JSTaggedValue Evaluate(JSThread *thread, const JSHandle &module); static JSTaggedValue GetNamespace(JSTaggedValue module); static void SetNamespace(JSThread *thread, JSTaggedValue module, JSTaggedValue value); diff --git a/ecmascript/module/js_module_source_text.cpp b/ecmascript/module/js_module_source_text.cpp index dcb57aac3c..1ada5e417d 100644 --- a/ecmascript/module/js_module_source_text.cpp +++ b/ecmascript/module/js_module_source_text.cpp @@ -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 module = JSHandle::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> stack; // 4. Let result be InnerModuleInstantiation(module, stack, 0). @@ -497,8 +503,10 @@ int SourceTextModule::Instantiate(JSThread *thread, const JSHandleHasPendingException()) { 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 module = JSHandle::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> 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 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 module = JSHandle::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 module = JSHandle::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 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 moduleNamespace(thread, module->GetNamespace().GetWeakRawValue()); // If namespace is undefined, then if (moduleNamespace->IsUndefined()) { @@ -945,20 +956,12 @@ JSHandle SourceTextModule::GetModuleNamespace(JSThread *thread, return moduleNamespace; } -int SourceTextModule::Evaluate(JSThread *thread, const JSHandle &module, - const void *buffer, size_t size, bool excuteFromJob) +void SourceTextModule::HandleEvaluateResult(JSThread *thread, JSHandle &module, + JSHandle &capability, const CVector> &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> stack; - // 4. Let result be InnerModuleEvaluation(module, stack, 0) - JSHandle moduleRecord = JSHandle::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 JSHandleGetStatus(); ASSERT(status == ModuleStatus::EVALUATED && module->GetEvaluationError() == result); - // c. return result - return result; + //d. Perform ! Call(capability.[[Reject]], undefined, « result.[[Value]] »). + JSHandle reject(thread, capability->GetReject()); + JSHandle 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 &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 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> stack; + // 6. Let capability be ! NewPromiseCapability(%Promise%). + auto vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle capability = + JSPromise::NewPromiseCapability(thread, JSHandle::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 = JSHandle::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 &module, @@ -991,51 +1046,47 @@ int SourceTextModule::EvaluateForConcurrent(JSThread *thread, const JSHandleGetStatus(); ASSERT((status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATED)); - // 3. Let stack be a new empty List. - CVector> stack; // 4. Let result be InnerModuleEvaluation(module, stack, 0) JSHandle moduleRecord = JSHandle::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, CVector> &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::Cast(moduleRecord)); + // a. Let promise be ! module.Evaluate(). + JSTaggedValue promise = ModuleRecord::Evaluate(thread, JSHandle::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 error = + factory->GetJSError(base::ErrorType::ERROR, nullptr); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), promiseResult.GetInt()); + } + // d. Return index. return index; } + JSHandle module = JSHandle::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 JSHandleSetDFSIndex(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 requestedModules(thread, module->GetRequestedModules()); size_t requestedModulesLen = requestedModules->GetLength(); @@ -1092,25 +1145,48 @@ int SourceTextModule::InnerModuleEvaluation(JSThread *thread, const JSHandleSetStatus(ModuleStatus::EVALUATED); continue; } - // c. Set index to ? InnerModuleEvaluation(requiredModule, stack, index). + // b. Set index to ? InnerModuleEvaluation(requiredModule, stack, index). JSHandle requiredModuleRecord = JSHandle::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 JSHandleGetPendingAsyncDependencies(); + 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 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 &module, + const CVector> &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, - CVector> &stack, int index, - const JSHandle &method) + int index, const JSHandle &method) { JSHandle module = JSHandle::Cast(moduleRecord); if (!module->GetRequestedModules().IsUndefined()) { @@ -1184,7 +1317,10 @@ int SourceTextModule::ModuleEvaluation(JSThread *thread, const JSHandle requiredModuleRecord = JSHandle::Cast(requiredModule); - index = SourceTextModule::InnerModuleEvaluation(thread, requiredModuleRecord, stack, index); + CVector> 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 &module, - const void *buffer, size_t size, bool excuteFromJob) +Expected SourceTextModule::ModuleExecution(JSThread *thread, + const JSHandle &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 &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 &module, + JSHandle &parent) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue asyncParentModules = module->GetAsyncParentModules(); + if (asyncParentModules.IsUndefined()) { + JSHandle array = factory->NewTaggedArray(1); + array->Set(thread, 0, parent.GetTaggedValue()); + module->SetAsyncParentModules(thread, array); + } else { + JSHandle 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 &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; + 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 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 promise(thread, result.Value()); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle onFulfilled = + factory->CreateJSAsyncModuleFulfilledFunction(); + onFulfilled->SetModule(thread, module); + + JSHandle onRejected = + factory->CreateJSAsyncModuleRejectedFunction(); + onRejected->SetModule(thread, module); + JSHandle tcap = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_IF_ABRUPT_COMPLETION(thread); + builtins::BuiltinsPromise::PerformPromiseThen( + thread, promise, JSHandle::Cast(onFulfilled), + JSHandle::Cast(onRejected), tcap); +} + +void SourceTextModule::GatherAvailableAncestors(JSThread *thread, const JSHandle &module, + AsyncParentCompletionSet &execList) +{ + auto globalConstants = thread->GlobalConstants(); + JSTaggedValue asyncParentModulesValue = module->GetAsyncParentModules(); + if (asyncParentModulesValue.IsUndefined()) { + return; + } + JSMutableHandle cycleRoot(thread, globalConstants->GetUndefined()); + JSHandle asyncParentModules(thread, asyncParentModulesValue); + size_t asyncParentModulesLen = asyncParentModules->GetLength(); + // 1. For each Cyclic Module Record m of module.[[AsyncParentModules]], do + for (size_t idx = 0; idx < asyncParentModulesLen; idx++) { + JSHandle parentModule(thread, asyncParentModules->Get(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 &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 topLevelCapability(thread, topLevelCapabilityValue); + JSHandle resolve(thread, topLevelCapability->GetResolve()); + JSHandle 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 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 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 topLevelCapability(thread, capabilityValue); + JSHandle resolve(thread, topLevelCapability->GetResolve()); + JSHandle 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 &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 parentModule(thread, globalConstants->GetUndefined()); + JSHandle asyncParentModules(thread, asyncParentModulesValue); + size_t asyncParentModulesLen = asyncParentModules->GetLength(); + for (size_t idx = 0; idx < asyncParentModulesLen; idx++) { + parentModule.Update(asyncParentModules->Get(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 exceptionHandle(thread, error); + // if caught exceptionHandle type is JSError + if (exceptionHandle->IsJSError()) { + thread->GetCurrentEcmaContext()->HandleUncaughtException(error); + } + ASSERT(JSTaggedValue::SameValue(module->GetCycleRoot(), module.GetTaggedValue())); + JSHandle topLevelCapability(thread, topLevelCapabilityValue); + JSHandle reject(thread, topLevelCapability->GetReject()); + JSHandle 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 fulfilledFunc = + JSHandle::Cast(base::BuiltinsBase::GetConstructor(argv)); + JSHandle 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 rejectedFunc = + JSHandle::Cast(base::BuiltinsBase::GetConstructor(argv)); + JSHandle module(thread, rejectedFunc->GetModule()); + [[maybe_unused]] JSHandle value = base::BuiltinsBase::GetCallArg(argv, 0); + AsyncModuleExecutionRejected(thread, module, value.GetTaggedValue()); + return JSTaggedValue::Undefined(); +} } // namespace panda::ecmascript diff --git a/ecmascript/module/js_module_source_text.h b/ecmascript/module/js_module_source_text.h index 80fd069f07..f1a2cfeb8b 100644 --- a/ecmascript/module/js_module_source_text.h +++ b/ecmascript/module/js_module_source_text.h @@ -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 &lhs, const JSHandle &rhs) const + { + return lhs->GetAsyncEvaluatingOrdinal() < rhs->GetAsyncEvaluatingOrdinal(); + } + }; + using AsyncParentCompletionSet = + CSet, 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 &module, - const void *buffer = nullptr, size_t size = 0, bool excuteFromJob = false); - + static Expected ModuleExecution(JSThread *thread, const JSHandle &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 &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 &module, + AsyncParentCompletionSet &execList); + + // 16.2.1.5.3.4 AsyncModuleExecutionFulfilled ( module ) + static void AsyncModuleExecutionFulfilled(JSThread *thread, const JSHandle &module); + + // 16.2.1.5.3.5 AsyncModuleExecutionRejected ( module, error ) + static void AsyncModuleExecutionRejected(JSThread *thread, const JSHandle &module, + JSTaggedValue error); + + static JSTaggedValue AsyncModuleFulfilledFunc(EcmaRuntimeCallInfo *argv); + static JSTaggedValue AsyncModuleRejectedFunc(EcmaRuntimeCallInfo *argv); + static void AddAsyncParentModule(JSThread *thread, JSHandle &module, + JSHandle &parent); // 15.2.1.18 Runtime Semantics: GetModuleNamespace ( module ) static JSHandle GetModuleNamespace(JSThread *thread, const JSHandle &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 &module, - const void *buffer = nullptr, size_t size = 0, bool excuteFromJob = false); + static JSTaggedValue Evaluate(JSThread *thread, const JSHandle &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 &moduleHdl, @@ -186,8 +231,7 @@ public: static int EvaluateForConcurrent(JSThread *thread, const JSHandle &module, const JSHandle &method); static int ModuleEvaluation(JSThread *thread, const JSHandle &moduleRecord, - CVector> &stack, int index, - const JSHandle &method); + int index, const JSHandle &method); private: static void SetExportName(JSThread *thread, @@ -228,6 +272,12 @@ private: int &index, bool excuteFromJob); static int HandleInstantiateException(JSHandle &module, const CVector> &stack, int result); + static void HandleEvaluateResult(JSThread *thread, JSHandle &module, + JSHandle &capability, + const CVector> &stack, int result); + static void HandleConcurrentEvaluateResult(JSThread *thread, JSHandle &module, + const CVector> &stack, int result); + bool IsAsyncEvaluating(); }; class ResolvedBinding final : public Record { diff --git a/ecmascript/module/module_data_extractor.cpp b/ecmascript/module/module_data_extractor.cpp index 6e4eec59dc..5b597181e6 100644 --- a/ecmascript/module/module_data_extractor.cpp +++ b/ecmascript/module/module_data_extractor.cpp @@ -48,6 +48,9 @@ JSHandle ModuleDataExtractor::ParseModule(JSThread *thread, const JSHandle moduleRecord = factory->NewSourceTextModule(); ModuleDataExtractor::ExtractModuleDatas(thread, jsPandaFile, moduleId, moduleRecord); + bool hasTLA = jsPandaFile->GetHasTopLevelAwait(descriptor); + moduleRecord->SetHasTLA(hasTLA); + JSHandle ecmaModuleFilename = factory->NewFromUtf8(moduleFilename); moduleRecord->SetEcmaModuleFilename(thread, ecmaModuleFilename); diff --git a/ecmascript/module/tests/ecma_module_test.cpp b/ecmascript/module/tests/ecma_module_test.cpp index fcac7620ac..76203ddeac 100644 --- a/ecmascript/module/tests/ecma_module_test.cpp +++ b/ecmascript/module/tests/ecma_module_test.cpp @@ -279,10 +279,10 @@ HWTEST_F_L0(EcmaModuleTest, Instantiate_Evaluate_GetNamespace_SetNamespace) JSHandle module = moduleManager->HostGetImportedModule("module_test_module_test_C"); module->SetStatus(ModuleStatus::UNINSTANTIATED); ModuleRecord::Instantiate(thread, JSHandle(module)); - int res = ModuleRecord::Evaluate(thread, JSHandle(module)); + JSTaggedValue res = ModuleRecord::Evaluate(thread, JSHandle(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) diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index a5817b54a5..668abc08ff 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -1294,6 +1294,14 @@ void ObjectFactory::InitializeJSObject(const JSHandle &obj, const JSHa JSFunction::InitializeJSFunction(thread_, JSHandle(obj)); JSPromiseExecutorFunction::Cast(*obj)->SetCapability(thread_, JSTaggedValue::Undefined()); break; + case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION: + JSFunction::InitializeJSFunction(thread_, JSHandle(obj)); + JSAsyncModuleFulfilledFunction::Cast(*obj)->SetModule(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION: + JSFunction::InitializeJSFunction(thread_, JSHandle(obj)); + JSAsyncModuleRejectedFunction::Cast(*obj)->SetModule(thread_, JSTaggedValue::Undefined()); + break; case JSType::JS_ASYNC_GENERATOR_RESUME_NEXT_RETURN_PROCESSOR_RST_FTN: JSFunction::InitializeJSFunction(thread_, JSHandle(obj)); JSAsyncGeneratorResNextRetProRstFtn::Cast(*obj)->SetAsyncGeneratorObject(thread_, @@ -2982,6 +2990,36 @@ JSHandle ObjectFactory::CreateJSPromiseExecutorFuncti return executorFunction; } +JSHandle ObjectFactory::CreateJSAsyncModuleFulfilledFunction() +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle hclass = JSHandle::Cast(env->GetAsyncModuleFulfilledFunctionClass()); + JSHandle fulfilledFunction = + JSHandle::Cast(NewJSObject(hclass)); + fulfilledFunction->SetModule(thread_, JSTaggedValue::Undefined()); + JSHandle function = JSHandle::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 ObjectFactory::CreateJSAsyncModuleRejectedFunction() +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle hclass = JSHandle::Cast(env->GetAsyncModuleRejectedFunctionClass()); + JSHandle rejectedFunction = + JSHandle::Cast(NewJSObject(hclass)); + rejectedFunction->SetModule(thread_, JSTaggedValue::Undefined()); + JSHandle function = JSHandle::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 ObjectFactory::NewJSPromiseAllResolveElementFunction() { JSHandle env = vm_->GetGlobalEnv(); @@ -4066,6 +4104,12 @@ JSHandle 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); diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index 621e8027d3..3e95d5cf63 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -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 CreateJSPromiseExecutorFunction(); + JSHandle CreateJSAsyncModuleFulfilledFunction(); + + JSHandle CreateJSAsyncModuleRejectedFunction(); + JSHandle NewJSPromiseAllResolveElementFunction(); JSHandle NewJSPromiseAnyRejectElementFunction(); diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index bdec6f930c..8544da7667 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -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 moduleFulfilledClass = + JSHandle::Cast(globalEnv->GetAsyncModuleFulfilledFunctionClass()); + JSHandle 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 moduleRejectedClass = + JSHandle::Cast(globalEnv->GetAsyncModuleRejectedFunctionClass()); + JSHandle 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 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 moduleSourceRecord = factory->NewSourceTextModule(); DUMP_FOR_HANDLE(moduleSourceRecord); break; -- Gitee