diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 90bf40f9f4e4fb56e07263499f889d9c49a12984..52cdf6b37dcbe347eb5eeec319ae2fc42f2ad982 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -4603,12 +4603,24 @@ inline GateRef StubBuilder::GetSharedType(GateRef module) Int32((1LU << SourceTextModule::IS_SHARED_TYPE_BITS) - 1)); } +inline GateRef StubBuilder::GetModuleStatus(GateRef module) +{ + GateRef bitfield = GetBitFieldFromSourceTextModule(module); + return Int32And(bitfield, Int32((1LU << SourceTextModule::STATUS_BITS) - 1)); +} + inline GateRef StubBuilder::IsSharedModule(GateRef module) { GateRef sharedType = GetSharedType(module); return Int32Equal(sharedType, Int32(static_cast(SharedTypes::SHARED_MODULE))); } +inline GateRef StubBuilder::NeedUpdate(GateRef module) +{ + GateRef status = GetModuleStatus(module); + return BitAnd(IsSharedModule(module), Int32LessThan(status, Int32(static_cast(ModuleStatus::EVALUATING)))); +} + inline GateRef StubBuilder::GetCjsModuleFunction(GateRef glue) { GateRef globalEnv = GetCurrentGlobalEnv(); diff --git a/ecmascript/compiler/stub_builder.cpp b/ecmascript/compiler/stub_builder.cpp index fc3618c2996397701b52ef86b9bf617f2bed6078..5d1a06a88b030c5e862a8824a58ba250c25c563e 100644 --- a/ecmascript/compiler/stub_builder.cpp +++ b/ecmascript/compiler/stub_builder.cpp @@ -12360,13 +12360,13 @@ GateRef StubBuilder::LoadExternalmodulevar(GateRef glue, GateRef index, GateRef BRANCH(TaggedIsHole(resolvedModuleOfHotReload), ¬LdEndExecPatchMain, ¬Hole); Bind(¬LdEndExecPatchMain); - Label isSharedModule(env); - Label notSharedModule(env); - BRANCH(IsSharedModule(*resolvedModule), &isSharedModule, ¬SharedModule); - Bind(&isSharedModule); + Label needUpdate(env); + Label noNeedUpdate(env); + BRANCH(NeedUpdate(*resolvedModule), &needUpdate, &noNeedUpdate); + Bind(&needUpdate); resolvedModule = CallNGCRuntime(glue, RTSTUB_ID(UpdateSharedModule), {glue, *resolvedModule}); - Jump(¬SharedModule); - Bind(¬SharedModule); + Jump(&noNeedUpdate); + Bind(&noNeedUpdate); result = GetModuleValue(glue, *resolvedModule, idxOfResolvedBinding); Jump(&exit); diff --git a/ecmascript/compiler/stub_builder.h b/ecmascript/compiler/stub_builder.h index a909b492ca9d3d7ddb2f7f7a2846928b78b7300f..b443dabb7d16ee529465dcccf5e92bc26da38383 100644 --- a/ecmascript/compiler/stub_builder.h +++ b/ecmascript/compiler/stub_builder.h @@ -1230,6 +1230,8 @@ public: inline GateRef IsCjsModule(GateRef module); inline GateRef GetSharedType(GateRef module); inline GateRef IsSharedModule(GateRef module); + inline GateRef GetModuleStatus(GateRef module); + inline GateRef NeedUpdate(GateRef module); inline GateRef GetCjsModuleFunction(GateRef glue); void ModuleEnvMustBeValid(GateRef glue, GateRef curEnv); GateRef SearchFromModuleCache(GateRef glue, GateRef moduleName); diff --git a/ecmascript/debugger/debugger_api.cpp b/ecmascript/debugger/debugger_api.cpp index 01962646c483c3ed58d5d5e6eea873d6a1bd4a1d..36ba3ca8a6bc060356a0d25fb72857b79f127434 100644 --- a/ecmascript/debugger/debugger_api.cpp +++ b/ecmascript/debugger/debugger_api.cpp @@ -522,7 +522,7 @@ JSHandle DebuggerApi::GetImportModule(const EcmaVM *ecmaVm, environment.Update(moduleEnvironment); JSTaggedValue resolvedBinding = environment->Get(thread, idx); ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject()); - importModule.Update(binding->GetModule(thread)); + importModule.Update(binding->GetIndexBindingModule(thread)); name = EcmaStringAccessor(importName).ToStdString(thread); return importModule; } diff --git a/ecmascript/module/js_module_manager.cpp b/ecmascript/module/js_module_manager.cpp index 7e3df3b1309fb2984ee775ee40ca5b6d6613e7bf..e473f4a0c7d55751b1cd51948713fa81f4c6ac33 100644 --- a/ecmascript/module/js_module_manager.cpp +++ b/ecmascript/module/js_module_manager.cpp @@ -102,11 +102,21 @@ JSTaggedValue ModuleManager::GetExternalModuleVarFastPathForJIT(JSThread *thread JSHandle ModuleManager::GetImportedModule(const CString &referencing) { auto thread = vm_->GetJSThread(); - if (!IsLocalModuleLoaded(referencing)) { - return SharedModuleManager::GetInstance()->GetSModule(thread, referencing); - } else { - return HostGetImportedModule(referencing); + JSHandle sharedModule = SharedModuleManager::GetInstance()->GetSModule(thread, referencing); + if (sharedModule.GetTaggedValue().IsUndefined()) { + return TryGetImportedLocalModule(referencing); + } + return sharedModule; +} + +JSHandle ModuleManager::TryGetImportedLocalModule(const CString &referencing) +{ + auto thread = vm_->GetJSThread(); + auto entry = resolvedModules_.Find(referencing); + if (!entry) { + return JSHandle(thread->GlobalConstants()->GetHandledUndefined()); } + return JSHandle(thread, entry.value()); } JSHandle ModuleManager::HostGetImportedModule(const CString &referencing) diff --git a/ecmascript/module/js_module_manager.h b/ecmascript/module/js_module_manager.h index 8311b783431af0f30184b766473ec32f28193b90..b58b17260ad50f8a8826f13e72fbd45547a28c97 100644 --- a/ecmascript/module/js_module_manager.h +++ b/ecmascript/module/js_module_manager.h @@ -141,6 +141,8 @@ private: void RemoveModuleNameFromList(const CString &recordName); + JSHandle TryGetImportedLocalModule(const CString &referencing); + static constexpr uint32_t DEAULT_DICTIONART_CAPACITY = 4; uint32_t nextModuleAsyncEvaluatingOrdinal_{SourceTextModule::FIRST_ASYNC_EVALUATING_ORDINAL}; diff --git a/ecmascript/module/js_module_namespace.cpp b/ecmascript/module/js_module_namespace.cpp index 377c8d3e823d830d36874f31e26dde5413ade276..2675ec504240fa0ca4d879812a1b9723de565462 100644 --- a/ecmascript/module/js_module_namespace.cpp +++ b/ecmascript/module/js_module_namespace.cpp @@ -140,7 +140,7 @@ OperationResult ModuleNamespace::GetProperty(JSThread *thread, const JSHandle resolvedBind = JSHandle::Cast(binding); - JSTaggedValue targetModule = resolvedBind->GetModule(thread); + JSTaggedValue targetModule = resolvedBind->GetIndexBindingModule(thread); // 9. Assert: targetModule is not undefined. ASSERT(!targetModule.IsUndefined()); JSHandle module(thread, targetModule); @@ -154,13 +154,6 @@ OperationResult ModuleNamespace::GetProperty(JSThread *thread, const JSHandle sharedModule = SharedModuleManager::GetInstance()->GetSModule( - thread, module->GetEcmaModuleRecordNameString()); - if (sharedModule.GetTaggedValue().IsSourceTextModule()) { - module = sharedModule; - } - } result = module->GetModuleValue(thread, resolvedBind->GetIndex(), true); } break; @@ -375,7 +368,7 @@ bool ModuleNamespace::ValidateKeysAvailable(JSThread *thread, const JSHandleIsResolvedBinding()) { targetModule = JSHandle::Cast(binding)->GetModule(thread); } else if (binding->IsResolvedIndexBinding()) { - targetModule = JSHandle::Cast(binding)->GetModule(thread); + targetModule = JSHandle::Cast(binding)->GetIndexBindingModule(thread); } ASSERT(!targetModule.IsUndefined()); JSTaggedValue dictionary = SourceTextModule::Cast(targetModule.GetTaggedObject())->GetNameDictionary(thread); diff --git a/ecmascript/module/js_module_source_text.cpp b/ecmascript/module/js_module_source_text.cpp index 790050a045867f15d510974b007e1a01d82ecdd6..df195c4b6e0189166ec07d1d6f946cc6b22d4368 100644 --- a/ecmascript/module/js_module_source_text.cpp +++ b/ecmascript/module/js_module_source_text.cpp @@ -1556,8 +1556,8 @@ JSHandle SourceTextModule::GetStarResolution(JSThread *thread, } else { JSHandle resolutionBd = JSHandle::Cast(resolution); JSHandle starResolutionBd = JSHandle::Cast(starResolution); - if ((!JSTaggedValue::SameValue(thread, resolutionBd->GetModule(thread), - starResolutionBd->GetModule(thread))) || + if ((!JSTaggedValue::SameValue(thread, resolutionBd->GetIndexBindingModule(thread), + starResolutionBd->GetIndexBindingModule(thread))) || resolutionBd->GetIndex() != starResolutionBd->GetIndex()) { return globalConstants->GetHandledAmbiguousString(); } @@ -2219,12 +2219,12 @@ JSHandle SourceTextModule::GetModuleFromCacheOrResolveNewOne(J { JSHandle request(thread, requestedModules->Get(thread, idx)); JSHandle requireModule; + if (request->IsSourceTextModule()) { + // normal module: directly get require SourceTextModule. + return JSHandle::Cast(request); + } if (!request->IsHole()) { - if (request->IsSourceTextModule()) { - // current is normal module: directly get require SourceTextModule. - return JSHandle::Cast(request); - } - // current is shared module: resolve or find request module by request string. + // have shared module: resolve or find request module by request string. ModuleManager *moduleManager = thread->GetModuleManager(); CString requestStr = ModulePathHelper::Utf8ConvertToString(thread, request.GetTaggedValue()); // may return undefined @@ -2234,15 +2234,11 @@ JSHandle SourceTextModule::GetModuleFromCacheOrResolveNewOne(J return requireModule; } /* - * case A import B, A is sharedModule, B is normalModule. - * Thread 1 Instantiate: 1. resolve A/B, add A to resolvedSharedModules_ , add B to resolvedModules_ - * 2. mark A/B as INSTANTIATED. - * Thread 1 Evaluate: Doesn't evaluate A immediately. - * Thread 2 Instantiate: find A in resolvedSharedModules_, it's stauts is INSTANTIATED, return; - * Thread 2 Evaluate: 1. evaluate B first, then evaluate A. - * 2. find B through [GetRequestedModuleFromCache], crash. - * Because [GetRequestedModuleFromCache] is a fastpath for resolvedModule, but B is not resolved in thread 2. - * In this case, we need to add resolve new module process. + * case shared module has re-export. + * A(normal module, import string) -> B(shared module, re-export string)-> C(normal module, export string) + * If shared module B is evaluatd in other thread. B won't Instantiate in current thread. + * A will create binding with C when instantiating. But C is not resolved in current thread. + * Therefore, need resolve one here. */ JSHandle moduleRequests(thread, module->GetModuleRequests(thread)); JSHandle required(thread, moduleRequests->Get(thread, idx)); @@ -2327,7 +2323,7 @@ JSHandle SourceTextModule::CreateBindingByIndexBinding(JSThread* JSHandle binding, bool isShared) { - JSHandle resolvedModule(thread, binding->GetModule(thread)); + JSHandle resolvedModule(thread, binding->GetIndexBindingModule(thread)); JSHandle bindingName = GetBindingNameByIndex(thread, resolvedModule, binding->GetIndex()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); @@ -2374,4 +2370,25 @@ JSHandle SourceTextModule::FindFuncInModuleForHook(JSThread* thre } return JSObject::FindFuncInObjectForHook(thread, exportEntity, className, funcName); } + +JSHandle SourceTextModule::UpdateSharedModule(JSThread *thread, JSHandle module) +{ + JSHandle sharedModule = SharedModuleManager::GetInstance()->GetSModule( + thread, module->GetEcmaModuleRecordNameString()); + // shared module may not evaluated if lazy, thus won't add to sharedModule map. + if (sharedModule.GetTaggedValue().IsSourceTextModule()) { + return sharedModule; + } + return module; +} + +// Attention: Use GetIndexBindingModule to get corresponding module rather than GetModule from field. +JSTaggedValue ResolvedIndexBinding::GetIndexBindingModule(JSThread *thread) +{ + JSHandle module(thread, GetModule(thread)); + if (SourceTextModule::NeedUpdate(module)) { + return SourceTextModule::UpdateSharedModule(thread, module).GetTaggedValue(); + } + return module.GetTaggedValue(); +} } // namespace panda::ecmascript diff --git a/ecmascript/module/js_module_source_text.h b/ecmascript/module/js_module_source_text.h index 0e22d94a4675947e88469870d41a83c19019f471..836afc9b7ee20cf44a01754f9732c7c282a60a3e 100644 --- a/ecmascript/module/js_module_source_text.h +++ b/ecmascript/module/js_module_source_text.h @@ -451,6 +451,13 @@ public: const std::string &className, const std::string &funcName); + static JSHandle UpdateSharedModule(JSThread *thread, JSHandle module); + + static bool NeedUpdate(JSHandle module) + { + return SourceTextModule::IsSharedModule(module) && module->GetStatus() < ModuleStatus::EVALUATING; + } + private: static JSHandle GetStarResolution(JSThread *thread, const JSHandle &exportName, @@ -523,6 +530,7 @@ public: CAST_CHECK(ResolvedIndexBinding, IsResolvedIndexBinding); static constexpr size_t MODULE_OFFSET = Record::SIZE; + // Use GetModule may cause problem if module is shared, please use GetIndexBindingModule. ACCESSORS(Module, MODULE_OFFSET, INDEX_OFFSET); ACCESSORS_PRIMITIVE_FIELD(Index, int32_t, INDEX_OFFSET, BIT_FIELD_OFFSET); ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, END_OFFSET) @@ -532,6 +540,9 @@ public: FIRST_BIT_FIELD(BitField, IsUpdatedFromResolvedBinding, bool, IS_UPDATED_FROM_RESOLVED_BINDING_BITS) DECL_DUMP() DECL_VISIT_OBJECT(MODULE_OFFSET, INDEX_OFFSET) + + // Attention: Use GetIndexBindingModule to get corresponding module rather than GetModule from field. + JSTaggedValue GetIndexBindingModule(JSThread *thread); }; } // namespace panda::ecmascript #endif // ECMASCRIPT_MODULE_JS_MODULE_SOURCE_TEXT_H diff --git a/ecmascript/module/js_shared_module.cpp b/ecmascript/module/js_shared_module.cpp index b7af5330ac6b7cd74b64efbca7d218b458a0532a..1c97f830f07419089207ddd5eee8afce559e826f 100644 --- a/ecmascript/module/js_shared_module.cpp +++ b/ecmascript/module/js_shared_module.cpp @@ -48,7 +48,7 @@ JSHandle SendableClassModule::CloneRecordIndexBinding(JSThread *t { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle binding(thread, indexBinding); - JSHandle resolvedModule(thread, binding->GetModule(thread)); + JSHandle resolvedModule(thread, binding->GetIndexBindingModule(thread)); if (SourceTextModule::IsSharedModule((resolvedModule))) { return JSHandle::Cast( factory->NewSResolvedIndexBindingRecord(resolvedModule, binding->GetIndex())); diff --git a/ecmascript/module/module_value_accessor.cpp b/ecmascript/module/module_value_accessor.cpp index 2f14161db676d592c43f9521da6b555ce9f33fec..beaba5c6220458475f6ea959803321e4e65ca221 100644 --- a/ecmascript/module/module_value_accessor.cpp +++ b/ecmascript/module/module_value_accessor.cpp @@ -295,7 +295,7 @@ template JSTaggedValue ModuleValueAccessor::GetModuleValueFromIndexBinding(const GetModuleValueFromBindingInfo &info) { JSHandle binding(info.thread, info.resolvedBinding); - JSMutableHandle resolvedModule(info.thread, binding->GetModule(info.thread)); + JSMutableHandle resolvedModule(info.thread, binding->GetIndexBindingModule(info.thread)); if (!info.isSendable && info.thread->GetStageOfHotReload() == StageOfHotReload::LOAD_END_EXECUTE_PATCHMAIN) { const JSHandle resolvedModuleOfHotReload = info.thread->GetEcmaVM()->FindPatchModule(resolvedModule->GetEcmaModuleRecordNameString()); @@ -309,12 +309,8 @@ JSTaggedValue ModuleValueAccessor::GetModuleValueFromIndexBinding(const GetModul EvaluateModuleIfNeeded(info.thread, resolvedModule); } RETURN_VALUE_IF_ABRUPT_COMPLETION(info.thread, JSTaggedValue::Exception()); - if (SourceTextModule::IsSharedModule(resolvedModule)) { - JSHandle sharedModule = SharedModuleManager::GetInstance()->GetSModule( - info.thread, resolvedModule->GetEcmaModuleRecordNameString()); - if (sharedModule.GetTaggedValue().IsSourceTextModule()) { - resolvedModule.Update(sharedModule); - } + if (SourceTextModule::NeedUpdate(resolvedModule)) { + resolvedModule.Update(SourceTextModule::UpdateSharedModule(info.thread, resolvedModule)); } LogModuleLoadInfo(info.thread, info.module, resolvedModule, info.index, info.isSendable); return GetModuleValue(info.thread, resolvedModule, binding->GetIndex()); diff --git a/ecmascript/module/tests/BUILD.gn b/ecmascript/module/tests/BUILD.gn index a7de95c55ada972f08a2715fb2406fbff8289eb0..341440a54d68c94f8472c1a75d74eb75ee69872e 100644 --- a/ecmascript/module/tests/BUILD.gn +++ b/ecmascript/module/tests/BUILD.gn @@ -52,6 +52,55 @@ foreach(file, test_js_files) { } } +test_multi_files = [ + "shared_test/single_instance_test_shared.js", + "shared_test/single_instance_test.js", + "shared_test/single_instance.js", + "shared_test/string.js", + "shared_test/entry.js", +] + +_test_js_file_name_ = [ + "shared_test" +] + +foreach(filename, _test_js_file_name_) { + merge_file = "$target_out_dir/${filename}.txt" + merge_file_prefix = "//arkcompiler/ets_runtime/ecmascript/module/tests/${filename}/" + _merge_file_raw_ = "//arkcompiler/ets_runtime/ecmascript/module/tests/${filename}/${filename}.txt" + + action("gen_${filename}_merge_file") { + script = "//arkcompiler/ets_runtime/test/quickfix/generate_merge_file.py" + args = [ + "--input", + rebase_path(_merge_file_raw_), + "--output", + rebase_path(merge_file), + "--prefix", + rebase_path(merge_file_prefix), + ] + + inputs = [ _merge_file_raw_ ] + outputs = [ merge_file ] + } + + abc_path = "$target_out_dir/${filename}.abc" + + es2abc_gen_abc("gen_${filename}_abc") { + extra_visibility = [ ":*" ] # Only targets in this file can depend on this. + extra_dependencies = [ ":gen_${filename}_merge_file" ] + src_js = "@" + rebase_path(merge_file) + dst_file = rebase_path(abc_path) + extra_args = [ + "--module", + "--merge-abc", + ] + + in_puts = test_multi_files + out_puts = [ abc_path ] + } +} + module_output_path = "ets_runtime" host_unittest_action("ModuleTest") { @@ -90,6 +139,10 @@ host_unittest_action("ModuleTest") { deps += [ ":gen_${file}_abc" ] } + deps += [ + ":gen_shared_test_abc", + ] + if (is_ohos && is_standard_system) { test_abc_dir = "/data/test" } else { diff --git a/ecmascript/module/tests/ecma_module_test.cpp b/ecmascript/module/tests/ecma_module_test.cpp index 62a63f593cd0ba88682feb0a06ce0fbac8b37fde..3fa77b69db07491f1b9101dd15d03ca47e47f4f5 100644 --- a/ecmascript/module/tests/ecma_module_test.cpp +++ b/ecmascript/module/tests/ecma_module_test.cpp @@ -20,6 +20,7 @@ #include "ecmascript/base/path_helper.h" #include "ecmascript/global_env.h" #include "ecmascript/jspandafile/js_pandafile.h" +#include "ecmascript/jspandafile/js_pandafile_executor.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/js_object-inl.h" @@ -47,6 +48,7 @@ using namespace panda::ecmascript; using namespace panda::panda_file; using namespace panda::pandasm; +using namespace testing::ext; namespace panda::test { using FunctionCallbackInfo = JSHandle(*)(JsiRuntimeCallInfo *); @@ -4354,4 +4356,293 @@ HWTEST_F_L0(EcmaModuleTest, DeregisterModuleList) EXPECT_EQ(vm->ContainInDeregisterModuleList("@ohos:hilog"), false); EXPECT_EQ(vm->ContainInDeregisterModuleList("com.bundleName.test/moduleName/requestModuleName"), false); } + +HWTEST_F_L0(EcmaModuleTest, NormalModuleReExecuteTest) +{ + CString baseFileName = MODULE_ABC_PATH "shared_test.abc"; + CString recordName = "single_instance_test"; + // normal module import first time + Expected result1 = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName); + EXPECT_TRUE(result1); + ModuleManager *moduleManager = thread->GetModuleManager(); + JSHandle module = moduleManager->GetImportedModule(recordName); + JSHandle moduleRecord = JSHandle::Cast(module); + JSTaggedValue val1 = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord); + EXPECT_EQ(val1, JSTaggedValue(1)); + // normal module import second time + Expected result2 = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName); + EXPECT_TRUE(result2); + JSTaggedValue val2 = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord); + EXPECT_EQ(val2, JSTaggedValue(2)); +} + +HWTEST_F(EcmaModuleTest, SharedModule1, TestSize.Level0) +{ + EcmaVM *vm = thread->GetEcmaVM(); + ObjectFactory *factory = vm->GetFactory(); + CString baseFileName = MODULE_ABC_PATH "shared_test.abc"; + CString recordName = "single_instance_test_shared"; + CString recordName1 = "entry"; + + // sharedmodule in uninstantiate + std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile( + thread, baseFileName, recordName1, false, ExecuteTypes::STATIC); + JSHandle sharedModuleRecord1 = ModuleResolver::HostResolveImportedModule( + thread, baseFileName, recordName, jsPandaFile.get(), ExecuteTypes::STATIC); + JSHandle sharedModule1 = JSHandle::Cast(sharedModuleRecord1); + EXPECT_EQ(sharedModule1->GetStatus(), ModuleStatus::UNINSTANTIATED); + + // switch moduleManager mock multi thread + ModuleManager *moduleManager1 = thread->GetModuleManager(); + ModuleManager *moduleManager2 = new ModuleManager(vm); + JSHandle nativePointer(vm->GetGlobalEnv()->GetModuleManagerNativePointer()); + nativePointer->SetExternalPointer(moduleManager2); + vm->AddModuleManager(moduleManager2); + EXPECT_NE(moduleManager1, thread->GetModuleManager()); + + // execute in new module manager + Expected result = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(result); + JSHandle sharedModule2 = + thread->GetModuleManager()->GetImportedModule(recordName); + JSHandle sharedModuleRecord2 = JSHandle::Cast(sharedModule2); + JSTaggedValue val = ModuleValueAccessor::GetModuleValueInner(thread, 0, sharedModuleRecord2); + EXPECT_EQ(val, JSTaggedValue(1)); + EXPECT_EQ(sharedModule2->GetStatus(), ModuleStatus::EVALUATED); + JSHandle module1 = thread->GetModuleManager()->GetImportedModule(recordName1); + JSHandle moduleRecord1 = JSHandle::Cast(module1); + JSHandle exportString(factory->NewFromUtf8("module string")); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord1); + EXPECT_EQ(val, exportString.GetTaggedValue()); + // switch moduleManager + nativePointer->SetExternalPointer(moduleManager1); + EXPECT_NE(moduleManager2, thread->GetModuleManager()); + // execute in old module manager (bug occurs need fix) + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(thread->HasPendingException()); + thread->ClearException(); + // check re-export, need execute string first(bug). + CString recordName4 = "string"; + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName4); + EXPECT_TRUE(result); + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(result); + + EXPECT_EQ(sharedModule1->GetStatus(), ModuleStatus::UNINSTANTIATED); + JSHandle updateModule = + thread->GetModuleManager()->GetImportedModule(recordName); + EXPECT_NE(updateModule, sharedModule1); + EXPECT_EQ(updateModule, sharedModule2); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, sharedModuleRecord2); + EXPECT_EQ(val, JSTaggedValue(1)); + JSHandle module2 = thread->GetModuleManager()->GetImportedModule(recordName1); + JSHandle moduleRecord2 = JSHandle::Cast(module2); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord2); + EXPECT_EQ(val, exportString.GetTaggedValue()); + EXPECT_NE(module1, module2); +} + +HWTEST_F(EcmaModuleTest, SharedModule2, TestSize.Level0) +{ + EcmaVM *vm = thread->GetEcmaVM(); + ObjectFactory *factory = vm->GetFactory(); + CString baseFileName = MODULE_ABC_PATH "shared_test.abc"; + CString recordName = "single_instance_test_shared"; + CString recordName1 = "entry"; + + // sharedmodule in instantiated + std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile( + thread, baseFileName, recordName1, false, ExecuteTypes::STATIC); + JSHandle moduleRecord = ModuleResolver::HostResolveImportedModule( + thread, baseFileName, recordName1, jsPandaFile.get(), ExecuteTypes::STATIC); + SourceTextModule::Instantiate(thread, moduleRecord, ExecuteTypes::STATIC); + EXPECT_TRUE(!thread->HasPendingException()); + JSHandle sharedModule1 = + thread->GetModuleManager()->GetImportedModule(recordName); + EXPECT_EQ(sharedModule1->GetStatus(), ModuleStatus::INSTANTIATED); + + // switch moduleManager mock multi thread + ModuleManager *moduleManager1 = thread->GetModuleManager(); + ModuleManager *moduleManager2 = new ModuleManager(vm); + JSHandle nativePointer(vm->GetGlobalEnv()->GetModuleManagerNativePointer()); + nativePointer->SetExternalPointer(moduleManager2); + vm->AddModuleManager(moduleManager2); + EXPECT_NE(moduleManager1, thread->GetModuleManager()); + + // execute in new module manager + Expected result = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(result); + JSHandle sharedModule2 = + thread->GetModuleManager()->GetImportedModule(recordName); + JSHandle sharedModuleRecord2 = JSHandle::Cast(sharedModule2); + JSTaggedValue val = ModuleValueAccessor::GetModuleValueInner(thread, 0, sharedModuleRecord2); + EXPECT_EQ(val, JSTaggedValue(1)); + EXPECT_EQ(sharedModule2->GetStatus(), ModuleStatus::EVALUATED); + JSHandle module1 = thread->GetModuleManager()->GetImportedModule(recordName1); + JSHandle moduleRecord1 = JSHandle::Cast(module1); + JSHandle exportString(factory->NewFromUtf8("module string")); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord1); + EXPECT_EQ(val, exportString.GetTaggedValue()); + // switch moduleManager + nativePointer->SetExternalPointer(moduleManager1); + EXPECT_NE(moduleManager2, thread->GetModuleManager()); + // execute in old module manager (bug occurs need fix) + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(thread->HasPendingException()); + thread->ClearException(); + // check re-export, need execute string first(bug). + CString recordName4 = "string"; + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName4); + EXPECT_TRUE(result); + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(result); + + EXPECT_EQ(sharedModule1->GetStatus(), ModuleStatus::INSTANTIATED); + JSHandle updateModule = + thread->GetModuleManager()->GetImportedModule(recordName); + EXPECT_NE(updateModule, sharedModule1); + EXPECT_EQ(updateModule, sharedModule2); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, sharedModuleRecord2); + EXPECT_EQ(val, JSTaggedValue(1)); + JSHandle module2 = thread->GetModuleManager()->GetImportedModule(recordName1); + JSHandle moduleRecord2 = JSHandle::Cast(module2); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord2); + EXPECT_EQ(val, exportString.GetTaggedValue()); + EXPECT_NE(module1, module2); +} + +HWTEST_F(EcmaModuleTest, SharedModule3, TestSize.Level0) +{ + EcmaVM *vm = thread->GetEcmaVM(); + ObjectFactory *factory = vm->GetFactory(); + CString baseFileName = MODULE_ABC_PATH "shared_test.abc"; + CString recordName = "single_instance_test_shared"; + CString recordName1 = "entry"; + + // sharedmodule in evaluate + Expected result = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(result); + JSHandle sharedModule1 = + thread->GetModuleManager()->GetImportedModule(recordName); + EXPECT_EQ(sharedModule1->GetStatus(), ModuleStatus::EVALUATED); + JSHandle module1 = thread->GetModuleManager()->GetImportedModule(recordName1); + JSHandle moduleRecord1 = JSHandle::Cast(module1); + JSHandle exportString(factory->NewFromUtf8("module string")); + JSTaggedValue val = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord1); + EXPECT_EQ(val, exportString.GetTaggedValue()); + JSHandle sharedModuleRecord1 = JSHandle::Cast(sharedModule1); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, sharedModuleRecord1); + EXPECT_EQ(val, JSTaggedValue(1)); + + // switch moduleManager mock multi thread + ModuleManager *moduleManager1 = thread->GetModuleManager(); + ModuleManager *moduleManager2 = new ModuleManager(vm); + JSHandle nativePointer(vm->GetGlobalEnv()->GetModuleManagerNativePointer()); + nativePointer->SetExternalPointer(moduleManager2); + vm->AddModuleManager(moduleManager2); + EXPECT_NE(moduleManager1, thread->GetModuleManager()); + + // execute in new module manager + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(!result); + EXPECT_TRUE(thread->HasPendingException()); + thread->ClearException(); + // check re-export, need execute string first(bug). + CString recordName4 = "string"; + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName4); + EXPECT_TRUE(result); + result = JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(result); + JSHandle sharedModule2 = + thread->GetModuleManager()->GetImportedModule(recordName); + EXPECT_EQ(sharedModule1, sharedModule2); + JSHandle sharedModuleRecord2 = JSHandle::Cast(sharedModule2); + EXPECT_EQ(sharedModule2->GetStatus(), ModuleStatus::EVALUATED); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, sharedModuleRecord2); + EXPECT_EQ(val, JSTaggedValue(1)); + JSHandle module2 = thread->GetModuleManager()->GetImportedModule(recordName1); + JSHandle moduleRecord2 = JSHandle::Cast(module2); + val = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord2); + EXPECT_EQ(val, exportString.GetTaggedValue()); +} + +HWTEST_F(EcmaModuleTest, GetIndexBindingModule, TestSize.Level0) +{ + EcmaVM *vm = thread->GetEcmaVM(); + ObjectFactory *objectFactory = vm->GetFactory(); + JSHandle module1 = objectFactory->NewSSourceTextModule(); + CString recordName1 = "sharedModule1"; + module1->SetEcmaModuleRecordNameString(recordName1); + module1->SetSharedType(SharedTypes::SHARED_MODULE); + module1->SetStatus(ModuleStatus::EVALUATED); + JSHandle resolvedBinding = JSHandle::Cast( + objectFactory->NewResolvedIndexBindingRecord(module1, 0)); + JSHandle resolutionBd = JSHandle::Cast(resolvedBinding); + // no update + JSTaggedValue res = resolutionBd->GetIndexBindingModule(thread); + EXPECT_EQ(res, module1.GetTaggedValue()); + // no sharedmodule in list, thus don't update + module1->SetStatus(ModuleStatus::INSTANTIATED); + res = resolutionBd->GetIndexBindingModule(thread); + EXPECT_EQ(res, module1.GetTaggedValue()); + + JSHandle module2 = objectFactory->NewSSourceTextModule(); + module2->SetEcmaModuleRecordNameString(recordName1); + module2->SetSharedType(SharedTypes::SHARED_MODULE); + module2->SetStatus(ModuleStatus::EVALUATED); + SharedModuleManager::GetInstance()-> + TransferFromLocalToSharedModuleMapAndGetInsertedSModule(thread, module2); + // have sharedmodule in list, update + res = resolutionBd->GetIndexBindingModule(thread); + EXPECT_EQ(res, module2.GetTaggedValue()); + + JSHandle module3 = objectFactory->NewSourceTextModule(); + CString recordName2 = "normalmodule"; + module3->SetEcmaModuleRecordNameString(recordName2); + module3->SetStatus(ModuleStatus::EVALUATED); + JSHandle module4 = objectFactory->NewSSourceTextModule(); + module4->SetEcmaModuleRecordNameString(recordName2); + module4->SetSharedType(SharedTypes::SHARED_MODULE); + module4->SetStatus(ModuleStatus::EVALUATED); + SharedModuleManager::GetInstance()-> + TransferFromLocalToSharedModuleMapAndGetInsertedSModule(thread, module4); + resolvedBinding = JSHandle::Cast(objectFactory->NewResolvedIndexBindingRecord(module3, 0)); + resolutionBd = JSHandle::Cast(resolvedBinding); + // normal module won't update + res = resolutionBd->GetIndexBindingModule(thread); + EXPECT_EQ(res, module3.GetTaggedValue()); +} + +HWTEST_F(EcmaModuleTest, GetImportedModule, TestSize.Level0) +{ + EcmaVM *vm = thread->GetEcmaVM(); + ObjectFactory *factory = vm->GetFactory(); + ModuleManager *moduleManager = thread->GetModuleManager(); + CString recordName = "sharedModule1"; + // cannot find module + JSHandle res = moduleManager->GetImportedModule(recordName); + EXPECT_TRUE(res.GetTaggedValue().IsUndefined()); + JSHandle module1 = factory->NewSSourceTextModule(); + module1->SetEcmaModuleRecordNameString(recordName); + module1->SetSharedType(SharedTypes::SHARED_MODULE); + module1->SetStatus(ModuleStatus::EVALUATED); + SharedModuleManager::GetInstance()-> + TransferFromLocalToSharedModuleMapAndGetInsertedSModule(thread, module1); + // find shard module + res = moduleManager->GetImportedModule(recordName); + EXPECT_EQ(res, module1); + // find normal module + JSHandle module2 = factory->NewSourceTextModule(); + CString recordName1 = "normalmodule"; + module2->SetEcmaModuleRecordNameString(recordName1); + module2->SetStatus(ModuleStatus::EVALUATED); + moduleManager->AddResolveImportedModule(recordName1, module2.GetTaggedValue()); + res = moduleManager->GetImportedModule(recordName1); + EXPECT_EQ(res, module2); +} } // namespace panda::test diff --git a/ecmascript/module/tests/module_snapshot_test.cpp b/ecmascript/module/tests/module_snapshot_test.cpp index 4fd9a00eb34a8d87dfab5d625980b088788a4674..64c12cc0d37e6a652f88da7b55dcac44ba12b530 100644 --- a/ecmascript/module/tests/module_snapshot_test.cpp +++ b/ecmascript/module/tests/module_snapshot_test.cpp @@ -253,8 +253,10 @@ public: auto serializeBinding = ResolvedIndexBinding::Cast(serializeResolvedBinding.GetTaggedObject()); auto deserializeBinding = ResolvedIndexBinding::Cast(deserializeResolvedBinding.GetTaggedObject()); EXPECT_EQ(serializeBinding->GetIndex(), deserializeBinding->GetIndex()); - JSHandle seriBindingModule(thread, serializeBinding->GetModule(thread)); - JSHandle deSeriBindingModule(thread, deserializeBinding->GetModule(thread)); + JSHandle seriBindingModule( + thread, serializeBinding->GetIndexBindingModule(thread)); + JSHandle deSeriBindingModule( + thread, deserializeBinding->GetIndexBindingModule(thread)); CheckModule(seriBindingModule, deSeriBindingModule); } else if (serializeResolvedBinding.IsResolvedBinding()) { ASSERT_TRUE(serializeResolvedBinding.IsResolvedBinding()); @@ -387,7 +389,7 @@ public: ASSERT_TRUE(serializeResolvedBinding.IsResolvedIndexBinding()); auto serializeBinding = ResolvedIndexBinding::Cast(serializeResolvedBinding.GetTaggedObject()); ASSERT_TRUE(serializeBinding->GetIsUpdatedFromResolvedBinding()); - ASSERT_EQ(serializeBinding->GetModule(thread), serializeNativeModule.GetTaggedValue()); + ASSERT_EQ(serializeBinding->GetIndexBindingModule(thread), serializeNativeModule.GetTaggedValue()); // serialize and persist ASSERT_TRUE(MockModuleSnapshot::SerializeDataAndSaving(vm, path, version)); // after serialize, indexBinding has been restore to binding diff --git a/ecmascript/module/tests/shared_test/entry.js b/ecmascript/module/tests/shared_test/entry.js new file mode 100644 index 0000000000000000000000000000000000000000..5415b8d7d33783a8d4c1bce5c97154fe622dc7bf --- /dev/null +++ b/ecmascript/module/tests/shared_test/entry.js @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {stringOut} from './single_instance_test_shared' +let string = stringOut; +export {string}; \ No newline at end of file diff --git a/ecmascript/module/tests/shared_test/shared_test.txt b/ecmascript/module/tests/shared_test/shared_test.txt new file mode 100644 index 0000000000000000000000000000000000000000..84f46ebe7aec720847470728a33c09a2ee65aa3f --- /dev/null +++ b/ecmascript/module/tests/shared_test/shared_test.txt @@ -0,0 +1,5 @@ +single_instance.js;single_instance;esm;single_instance.js;xxsingle_instancexx +single_instance_test_shared.js;single_instance_test_shared;esm;single_instance_test_shared.js;xxsingle_instance_test_sharedxx +single_instance_test.js;single_instance_test;esm;single_instance_test.js;xxsingle_instance_testxx +string.js;string;esm;string.js;xxstringxx +entry.js;entry;esm;entry.js;xxentryxx \ No newline at end of file diff --git a/ecmascript/module/tests/shared_test/single_instance.js b/ecmascript/module/tests/shared_test/single_instance.js new file mode 100644 index 0000000000000000000000000000000000000000..f0b14b7fcfb68a3adb0eabcdb36a322e9112fa45 --- /dev/null +++ b/ecmascript/module/tests/shared_test/single_instance.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + static a = 0; + add() { + return A.a++; + } +} +export {A}; +export {stringOut} from './string'; \ No newline at end of file diff --git a/ecmascript/module/tests/shared_test/single_instance_test.js b/ecmascript/module/tests/shared_test/single_instance_test.js new file mode 100644 index 0000000000000000000000000000000000000000..5c2f5ba19abfa5e36e4125126b79fcbb61f32643 --- /dev/null +++ b/ecmascript/module/tests/shared_test/single_instance_test.js @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {A} from './single_instance' +let instanceA = new A(); +instanceA.add(); +export let normalA = A.a; \ No newline at end of file diff --git a/ecmascript/module/tests/shared_test/single_instance_test_shared.js b/ecmascript/module/tests/shared_test/single_instance_test_shared.js new file mode 100644 index 0000000000000000000000000000000000000000..227b2b85b0ce253c47b0b5d53cc69d1054506f9b --- /dev/null +++ b/ecmascript/module/tests/shared_test/single_instance_test_shared.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {A} from './single_instance' +export {stringOut} from './single_instance' +'use shared'; +let instanceA = new A(); +instanceA.add(); + +export let normalA = A.a; + +export function zoo() { + 'use sendable'; + return 'zoo'; +} \ No newline at end of file diff --git a/ecmascript/module/tests/shared_test/string.js b/ecmascript/module/tests/shared_test/string.js new file mode 100644 index 0000000000000000000000000000000000000000..6bc69247f68a7aa902a302005ef6f95c31b0dfd8 --- /dev/null +++ b/ecmascript/module/tests/shared_test/string.js @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +let stringOut = 'module string'; +export {stringOut}; diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index 4f213f59f51f88a4faf33212bbd8a087536a6d01..122adebccb7844d530e7b5be99308783fdacd657 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -4976,12 +4976,7 @@ JSTaggedValue RuntimeStubs::UpdateSharedModule(uintptr_t argGlue, JSTaggedValue DISALLOW_GARBAGE_COLLECTION; auto thread = JSThread::GlueToJSThread(argGlue); JSHandle module(thread, resolvedModule); - JSHandle sharedModule = - SharedModuleManager::GetInstance()->GetSModule(thread, module->GetEcmaModuleRecordNameString()); - if (sharedModule.GetTaggedValue().IsSourceTextModule()) { - return sharedModule.GetTaggedValue(); - } - return module.GetTaggedValue(); + return SourceTextModule::UpdateSharedModule(thread, module).GetTaggedValue(); } void RuntimeStubs::FatalPrintMisstakenResolvedBinding(int32_t index, JSTaggedValue curModule)