From c31dac557e9b7982adad39bb23f172905f8f8e8e Mon Sep 17 00:00:00 2001 From: yaochaonan Date: Wed, 23 Jul 2025 11:49:03 +0800 Subject: [PATCH] as Signed-off-by: yaochaonan Change-Id: If6080877528be21cae4020b77a7633049e09c93f --- ecmascript/compiler/stub_builder-inl.h | 12 + ecmascript/compiler/stub_builder.cpp | 12 +- ecmascript/compiler/stub_builder.h | 2 + ecmascript/debugger/debugger_api.cpp | 2 +- ecmascript/module/js_module_manager.cpp | 18 +- ecmascript/module/js_module_manager.h | 2 + ecmascript/module/js_module_namespace.cpp | 11 +- ecmascript/module/js_module_source_text.cpp | 41 +- ecmascript/module/js_module_source_text.h | 11 + ecmascript/module/js_shared_module.cpp | 2 +- ecmascript/module/module_value_accessor.cpp | 10 +- ecmascript/module/tests/BUILD.gn | 55 ++- ecmascript/module/tests/ecma_module_test.cpp | 439 +++++++++++++----- .../module/tests/module_snapshot_test.cpp | 6 +- .../module/tests/module_test/import_shared.js | 18 + .../tests/module_test/single_instance.js | 23 + .../tests/module_test/single_instance_test.js | 19 + .../single_instance_test_shared.js | 27 ++ ecmascript/module/tests/shared_test/entry.js | 18 + .../module/tests/shared_test/shared_test.txt | 5 + .../tests/shared_test/single_instance.js | 23 + .../tests/shared_test/single_instance_test.js | 19 + .../single_instance_test_shared.js | 27 ++ ecmascript/module/tests/shared_test/string.js | 17 + ecmascript/stubs/runtime_stubs.cpp | 7 +- 25 files changed, 660 insertions(+), 166 deletions(-) create mode 100644 ecmascript/module/tests/module_test/import_shared.js create mode 100644 ecmascript/module/tests/module_test/single_instance.js create mode 100644 ecmascript/module/tests/module_test/single_instance_test.js create mode 100644 ecmascript/module/tests/module_test/single_instance_test_shared.js create mode 100644 ecmascript/module/tests/shared_test/entry.js create mode 100644 ecmascript/module/tests/shared_test/shared_test.txt create mode 100644 ecmascript/module/tests/shared_test/single_instance.js create mode 100644 ecmascript/module/tests/shared_test/single_instance_test.js create mode 100644 ecmascript/module/tests/shared_test/single_instance_test_shared.js create mode 100644 ecmascript/module/tests/shared_test/string.js diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 71ab0f2d71..6ecc1767b7 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -4589,12 +4589,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 9968d3cbb7..aae0f8ffa7 100644 --- a/ecmascript/compiler/stub_builder.cpp +++ b/ecmascript/compiler/stub_builder.cpp @@ -12352,13 +12352,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 956668e6c8..78fbdaf118 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 ede12c47c2..3988dc9608 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 fbb25daca0..4eb7813449 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 8311b78343..b58b17260a 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 377c8d3e82..2675ec5042 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 1fe6fc22b8..f5cd03489a 100644 --- a/ecmascript/module/js_module_source_text.cpp +++ b/ecmascript/module/js_module_source_text.cpp @@ -1508,8 +1508,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(); } @@ -2186,15 +2186,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)); @@ -2279,7 +2275,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(); @@ -2326,4 +2322,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 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 c25a2cbd84..a24fdc013d 100644 --- a/ecmascript/module/js_module_source_text.h +++ b/ecmascript/module/js_module_source_text.h @@ -449,6 +449,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, @@ -521,6 +528,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) @@ -530,6 +538,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 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 b7af5330ac..1c97f830f0 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 2f14161db6..beaba5c622 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 b447918a41..7b922f2003 100644 --- a/ecmascript/module/tests/BUILD.gn +++ b/ecmascript/module/tests/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2025 Huawei Device Co., Ltd. +# Copyright (c) 2022 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 @@ -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") { @@ -85,6 +134,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 ac3fdd5134..2bc4affec5 100644 --- a/ecmascript/module/tests/ecma_module_test.cpp +++ b/ecmascript/module/tests/ecma_module_test.cpp @@ -15,39 +15,40 @@ #include "assembler/assembly-emitter.h" #include "assembler/assembly-parser.h" -#include "libpandafile/class_data_accessor-inl.h" - #include "ecmascript/base/path_helper.h" +#include "ecmascript/checkpoint/thread_state_transition.h" +#include "ecmascript/containers/containers_errors.h" +#include "ecmascript/dfx/native_module_failure_info.h" +#include "ecmascript/ecma_vm.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" -#include "ecmascript/module/js_module_manager.h" -#include "ecmascript/module/js_module_source_text.h" -#include "ecmascript/module/module_data_extractor.h" -#include "ecmascript/module/module_logger.h" -#include "ecmascript/module/module_path_helper.h" -#include "ecmascript/tests/test_helper.h" #include "ecmascript/linked_hash_table.h" -#include "ecmascript/checkpoint/thread_state_transition.h" #include "ecmascript/module/accessor/module_data_accessor.h" #include "ecmascript/module/js_module_deregister.h" +#include "ecmascript/module/js_module_manager.h" +#include "ecmascript/module/js_module_source_text.h" #include "ecmascript/module/js_shared_module.h" #include "ecmascript/module/js_shared_module_manager.h" +#include "ecmascript/module/module_data_extractor.h" +#include "ecmascript/module/module_logger.h" +#include "ecmascript/module/module_path_helper.h" +#include "ecmascript/module/module_resolver.h" #include "ecmascript/module/module_tools.h" #include "ecmascript/require/js_cjs_module.h" +#include "ecmascript/tests/test_helper.h" +#include "libpandafile/class_data_accessor-inl.h" #include "ecmascript/module/module_value_accessor.h" -#include "ecmascript/module/module_resolver.h" #include "ecmascript/module/napi_module_loader.h" -#include "ecmascript/ecma_vm.h" using namespace panda::ecmascript; using namespace panda::panda_file; using namespace panda::pandasm; - namespace panda::test { -using FunctionCallbackInfo = Local(*)(JsiRuntimeCallInfo *); +using FunctionCallbackInfo = JSHandle(*)(JsiRuntimeCallInfo *); class EcmaModuleTest : public testing::Test { public: static void SetUpTestCase() @@ -70,43 +71,63 @@ public: TestHelper::DestroyEcmaVMWithScope(instance, scope); } - static Local MockRequireNapiUndefined(JsiRuntimeCallInfo *runtimeCallInfo); - static Local MockRequireNapiFailure(JsiRuntimeCallInfo *runtimeCallInfo); - static Local MockRequireNapiValue(JsiRuntimeCallInfo *runtimeCallInfo); - static Local MockRequireNapiException(JsiRuntimeCallInfo *runtimeCallInfo); + static JSHandle MockRequireNapiFailure(JsiRuntimeCallInfo *runtimeCallInfo); + static JSHandle MockRequireNapiValue(JsiRuntimeCallInfo *runtimeCallInfo); + static JSHandle MockRequireNapiException(JsiRuntimeCallInfo *runtimeCallInfo); EcmaVM *instance {nullptr}; ecmascript::EcmaHandleScope *scope {nullptr}; JSThread *thread {nullptr}; }; -Local EcmaModuleTest::MockRequireNapiUndefined(JsiRuntimeCallInfo *runtimeCallInfo) -{ - EcmaVM *vm = runtimeCallInfo->GetVM(); - return JSValueRef::Undefined(vm); -} - -Local EcmaModuleTest::MockRequireNapiFailure(JsiRuntimeCallInfo *runtimeCallInfo) +JSHandle EcmaModuleTest::MockRequireNapiFailure(JsiRuntimeCallInfo *runtimeCallInfo) { EcmaVM *vm = runtimeCallInfo->GetVM(); vm->SetErrorInfoEnhance(true); - Local exports = ObjectRef::CreateNativeModuleFailureInfo(vm, "NativeModuleFailureInfo"); - return exports; + CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, thread->GlobalConstants()->GetHandledUndefined()); + ecmascript::ThreadManagedScope managedScope(thread); + if (EcmaVM::GetErrorInfoEnhance()) { + JSHandle nativeModuleErrorFailureInfo = + NativeModuleFailureInfo::CreateNativeModuleFailureInfo(vm, "NativeModuleFailureInfo"); + return JSHandle::Cast(nativeModuleErrorFailureInfo); + } + return JSHandle(thread->GlobalConstants()->GetHandledUndefined()); } -Local EcmaModuleTest::MockRequireNapiValue(JsiRuntimeCallInfo *runtimeCallInfo) +JSHandle EcmaModuleTest::MockRequireNapiValue(JsiRuntimeCallInfo *runtimeCallInfo) { EcmaVM *vm = runtimeCallInfo->GetVM(); - Local localNameHandle = StringRef::NewFromUtf8(vm, "exportVal"); - return localNameHandle; + CROSS_THREAD_CHECK(vm); + ecmascript::ThreadManagedScope managedScope(thread); + ObjectFactory *factory = vm->GetFactory(); + JSHandle current(factory->NewFromUtf8("exportVal")); + return current; } -Local EcmaModuleTest::MockRequireNapiException(JsiRuntimeCallInfo *runtimeCallInfo) +JSHandle EcmaModuleTest::MockRequireNapiException(JsiRuntimeCallInfo *runtimeCallInfo) { EcmaVM *vm = runtimeCallInfo->GetVM(); - Local message = StringRef::NewFromUtf8(vm, "load native module failed."); - Local error = Exception::ReferenceError(vm, message); - JSNApi::ThrowException(vm, error); + CROSS_THREAD_CHECK(vm); + ecmascript::ThreadManagedScope managedScope(thread); + ObjectFactory *factory = vm->GetFactory(); + JSHandle message(factory->NewFromUtf8("load native module failed.")); + JSHandle error = factory->GetJSError(ErrorType::ERROR, "load native module failed.", StackCheck::NO); + JSHandle code(thread, JSTaggedValue(ecmascript::containers::ErrorFlag::REFERENCE_ERROR)); + JSHandle key = factory->NewFromUtf8("code"); + JSHandle name = factory->NewFromUtf8("name"); + JSHandle value = factory->NewFromUtf8("BusinessError"); + JSObject::CreateDataPropertyOrThrow(thread, error, JSHandle::Cast(key), code); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + JSObject::CreateDataPropertyOrThrow(thread, error, JSHandle::Cast(name), + JSHandle::Cast(value)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + JSTaggedValue err = error.GetTaggedValue(); + if (thread->HasPendingException()) { + LOG_ECMA(DEBUG) << "An exception has already occurred before, keep old exception here."; + } else { + thread->SetException(err); + } + return message; } @@ -1944,7 +1965,8 @@ HWTEST_F_L0(EcmaModuleTest, HostResolveImportedModuleWithMerge) ModuleResolver::HostResolveImportedModule(thread, module1, nativeName); EXPECT_TRUE(res1->IsSourceTextModule()); - thread->GetModuleManager()->AddResolveImportedModule(recordName2, module2.GetTaggedValue()); + thread->GetModuleManager()->AddResolveImportedModule( + recordName2, module2.GetTaggedValue()); JSHandle res2 = ModuleResolver::HostResolveImportedModule(thread, module1, nativeName); EXPECT_TRUE(res2->IsSourceTextModule()); @@ -1961,7 +1983,8 @@ HWTEST_F_L0(EcmaModuleTest, ModuleResolverHostResolveImportedModule) module2->SetTypes(ModuleTypes::NATIVE_MODULE); JSHandle nativeName = JSHandle::Cast(objectFactory->NewFromUtf8("@ohos:hilog")); - thread->GetModuleManager()->AddResolveImportedModule(recordName2, module2.GetTaggedValue()); + thread->GetModuleManager()->AddResolveImportedModule( + recordName2, module2.GetTaggedValue()); JSHandle res1 = ModuleResolver::HostResolveImportedModule(thread, module1, nativeName); EXPECT_TRUE(res1->IsSourceTextModule()); @@ -2020,16 +2043,6 @@ HWTEST_F_L0(EcmaModuleTest, ResolveNativeStarExport) EXPECT_TRUE(res2->IsNull()); } -HWTEST_F_L0(EcmaModuleTest, MakeInternalArgs) -{ - std::vector> arguments; - auto vm = thread->GetEcmaVM(); - CString soName = "@ohos:hilig"; - arguments.emplace_back(StringRef::NewFromUtf8(vm, soName.c_str())); - SourceTextModule::MakeInternalArgs(vm, arguments, soName); - EXPECT_TRUE(!thread->HasPendingException()); -} - HWTEST_F_L0(EcmaModuleTest, LoadNativeModuleImpl) { auto vm = thread->GetEcmaVM(); @@ -2051,9 +2064,10 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModuleImpl) EXPECT_TRUE(!thread->HasPendingException()); // internal module - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, StringRef::NewFromUtf8(vm, "requireNapi"), - FunctionRef::New(const_cast(vm), MockRequireNapiUndefined)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSTaggedValue::SetProperty(thread, global, keyValue, thread->GlobalConstants()->GetHandledUndefined()); module->SetEcmaModuleRecordNameString("@hms:xxxxx"); SourceTextModule::LoadNativeModuleImpl(vm, thread, module, ModuleTypes::INTERNAL_MODULE); EXPECT_TRUE(!thread->HasPendingException()); @@ -2078,9 +2092,14 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModuleMayThrowError1) module->SetLocalExportEntries(thread, localExportEntries); SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiFailure)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiFailure), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); SourceTextModule::LoadNativeModuleMayThrowError(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(thread->HasPendingException()); } @@ -2099,9 +2118,10 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModuleMayThrowError2) module->SetLocalExportEntries(thread, localExportEntries); SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiUndefined)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSTaggedValue::SetProperty(thread, global, keyValue, thread->GlobalConstants()->GetHandledUndefined()); SourceTextModule::LoadNativeModuleMayThrowError(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(thread->HasPendingException()); } @@ -2120,9 +2140,14 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModuleMayThrowError3) module->SetLocalExportEntries(thread, localExportEntries); SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiException)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiException), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); SourceTextModule::LoadNativeModuleMayThrowError(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(thread->HasPendingException()); } @@ -2141,9 +2166,14 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModuleMayThrowError4) module->SetLocalExportEntries(thread, localExportEntries); SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiValue)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiValue), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); SourceTextModule::LoadNativeModuleMayThrowError(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(!thread->HasPendingException()); } @@ -2162,9 +2192,14 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModule1) module->SetLocalExportEntries(thread, localExportEntries); SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiValue)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiValue), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); SourceTextModule::LoadNativeModule(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(!thread->HasPendingException()); } @@ -2183,9 +2218,14 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModule2) module->SetLocalExportEntries(thread, localExportEntries); SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiFailure)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiFailure), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); SourceTextModule::LoadNativeModule(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(!thread->HasPendingException()); } @@ -2216,9 +2256,14 @@ HWTEST_F_L0(EcmaModuleTest, EvaluateNativeModule2) SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); module->SetStatus(ModuleStatus::INSTANTIATED); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiFailure)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiFailure), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); SourceTextModule::EvaluateNativeModule(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(!thread->HasPendingException()); } @@ -2238,9 +2283,14 @@ HWTEST_F_L0(EcmaModuleTest, EvaluateNativeModule3) SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); module->SetStatus(ModuleStatus::INSTANTIATED); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiValue)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiValue), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); SourceTextModule::EvaluateNativeModule(thread, module, ModuleTypes::APP_MODULE); EXPECT_TRUE(!thread->HasPendingException()); } @@ -2398,7 +2448,8 @@ HWTEST_F_L0(EcmaModuleTest, ExecuteNativeModuleMayThrowError) thread, recordName); EXPECT_EQ(res.GetTaggedValue(), JSTaggedValue::Undefined()); - moduleManager->AddResolveImportedModule(recordName, module.GetTaggedValue()); + thread->GetModuleManager()->AddResolveImportedModule( + recordName, module.GetTaggedValue()); JSHandle res2 = moduleManager->ExecuteNativeModuleMayThrowError( thread, recordName); EXPECT_NE(res2.GetTaggedValue(), JSTaggedValue::Undefined()); @@ -2422,7 +2473,7 @@ HWTEST_F_L0(EcmaModuleTest, ExecuteNativeModule) ModuleManager *moduleManager = thread->GetModuleManager(); module->SetStatus(ecmascript::ModuleStatus::INSTANTIATED); - moduleManager->AddResolveImportedModule( + thread->GetModuleManager()->AddResolveImportedModule( recordName, module.GetTaggedValue()); moduleManager->ExecuteNativeModule(thread, recordName); module->SetStatus(ModuleStatus::EVALUATED); @@ -2486,9 +2537,12 @@ HWTEST_F_L0(EcmaModuleTest, ModuleLogger) { moduleLogger->InsertModuleLoadInfo(module2, module3, -1); moduleLogger->InsertModuleLoadInfo(module2, module3, 0); moduleLogger->PrintModuleLoadInfo(); - Local nativeFunc = SourceTextModule::GetRequireNativeModuleFunc(thread->GetEcmaVM(), - module3->GetTypes()); - bool isFunc = nativeFunc->IsFunction(thread->GetEcmaVM()); + auto globalConstants = thread->GetEcmaVM()->GetJSThread()->GlobalConstants(); + auto funcName = (module3->GetTypes() == ModuleTypes::NATIVE_MODULE) ? + globalConstants->GetHandledRequireNativeModuleString() : + globalConstants->GetHandledRequireNapiString(); + JSTaggedValue nativeFunc = funcName.GetTaggedValue(); + bool isFunc = nativeFunc.IsJSFunction(); EXPECT_EQ(isFunc, false); } @@ -2498,9 +2552,12 @@ HWTEST_F_L0(EcmaModuleTest, GetRequireNativeModuleFunc) { uint16_t registerNum = module->GetRegisterCounts(); module->SetStatus(ecmascript::ModuleStatus::INSTANTIATED); module->SetRegisterCounts(registerNum); - Local nativeFunc = SourceTextModule::GetRequireNativeModuleFunc(thread->GetEcmaVM(), - module->GetTypes()); - bool isFunc = nativeFunc->IsFunction(thread->GetEcmaVM()); + auto globalConstants = thread->GetEcmaVM()->GetJSThread()->GlobalConstants(); + auto funcName = (module->GetTypes() == ModuleTypes::NATIVE_MODULE) ? + globalConstants->GetHandledRequireNativeModuleString() : + globalConstants->GetHandledRequireNapiString(); + JSTaggedValue nativeFunc = funcName.GetTaggedValue(); + bool isFunc = nativeFunc.IsJSFunction(); EXPECT_EQ(isFunc, false); } @@ -2541,36 +2598,6 @@ HWTEST_F_L0(EcmaModuleTest, StoreModuleValue2) EXPECT_EQ(valueHandle1.GetTaggedValue(), loadValue1); } -HWTEST_F_L0(EcmaModuleTest, MakeAppArgs1) { - std::vector> arguments; - CString soPath = "@normalized:Y&&&libentry.so&"; - CString moduleName = "entry"; - CString requestName = "@normalized:"; - arguments.emplace_back(StringRef::NewFromUtf8(thread->GetEcmaVM(), soPath.c_str())); - SourceTextModule::MakeAppArgs(thread->GetEcmaVM(), arguments, soPath, moduleName, requestName); - std::string res1 = arguments[0]->ToString(thread->GetEcmaVM())->ToString(thread->GetEcmaVM()); - std::string res2 = arguments[1]->ToString(thread->GetEcmaVM())->ToString(thread->GetEcmaVM()); - std::string res3 = arguments[2]->ToString(thread->GetEcmaVM())->ToString(thread->GetEcmaVM()); - EXPECT_TRUE(res1 == "entry"); - EXPECT_TRUE(res2 == "true"); - EXPECT_TRUE(res3 == "/entry"); -} - -HWTEST_F_L0(EcmaModuleTest, MakeAppArgs2) { - std::vector> arguments; - CString soPath = "@app:com.example.myapplication/entry"; - CString moduleName = "entry"; - CString requestName = "@app:"; - arguments.emplace_back(StringRef::NewFromUtf8(thread->GetEcmaVM(), soPath.c_str())); - SourceTextModule::MakeAppArgs(thread->GetEcmaVM(), arguments, soPath, moduleName, requestName); - std::string res1 = arguments[0]->ToString(thread->GetEcmaVM())->ToString(thread->GetEcmaVM()); - std::string res2 = arguments[1]->ToString(thread->GetEcmaVM())->ToString(thread->GetEcmaVM()); - std::string res3 = arguments[2]->ToString(thread->GetEcmaVM())->ToString(thread->GetEcmaVM()); - EXPECT_TRUE(res1 == "entry"); - EXPECT_TRUE(res2 == "true"); - EXPECT_TRUE(res3 == "@app:com.example.myapplication"); -} - HWTEST_F_L0(EcmaModuleTest, ConcatHspFileNameCrossBundle) { CString bundleName = "com.example.application"; @@ -3900,9 +3927,10 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModuleImpl2) thread->SetModuleLogger(moduleLogger); // internal module - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, StringRef::NewFromUtf8(vm, "requireNapi"), - FunctionRef::New(const_cast(vm), MockRequireNapiUndefined)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSTaggedValue::SetProperty(thread, global, keyValue, thread->GlobalConstants()->GetHandledUndefined()); module->SetEcmaModuleRecordNameString("@hms:xxxxx"); int arkProperties = thread->GetEcmaVM()->GetJSOptions().GetArkProperties(); thread->GetEcmaVM()->GetJSOptions().SetArkProperties(arkProperties | ArkProperties::ENABLE_ESM_TRACE); @@ -3926,9 +3954,14 @@ HWTEST_F_L0(EcmaModuleTest, LoadNativeModule3) module->SetLocalExportEntries(thread, localExportEntries); SourceTextModule::StoreModuleValue(thread, module, 0, val); module->SetTypes(ModuleTypes::NATIVE_MODULE); - Local requireNapi = StringRef::NewFromUtf8(vm, "requireNapi"); - Local globalObject = JSNApi::GetGlobalObject(vm); - globalObject->Set(vm, requireNapi, FunctionRef::New(const_cast(vm), MockRequireNapiValue)); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle keyValue = JSHandle::Cast(objectFactory->NewFromUtf8("requireNapi")); + JSHandle current(objectFactory->NewJSFunction(globalEnv, + reinterpret_cast(Callback::RegisterCallback))); + JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast(MockRequireNapiValue), + nullptr, nullptr, 0); + JSTaggedValue::SetProperty(thread, global, keyValue, JSHandle(current)); int arkProperties = thread->GetEcmaVM()->GetJSOptions().GetArkProperties(); thread->GetEcmaVM()->GetJSOptions().SetArkProperties(arkProperties | ArkProperties::ENABLE_ESM_TRACE); SourceTextModule::LoadNativeModule(thread, module, ModuleTypes::APP_MODULE); @@ -4272,4 +4305,178 @@ 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_L0(EcmaModuleTest, SharedModule1) +{ + 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); + EXPECT_NE(moduleManager1, thread->GetModuleManager()); + + // execute in new module manager + Expected result = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, recordName1); + EXPECT_TRUE(result); +} + +// HWTEST_F_L0(EcmaModuleTest, SharedModule2) +// { +// 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); +// 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_L0(EcmaModuleTest, SharedModule3) +// { +// 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); +// 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()); +// } } // namespace panda::test diff --git a/ecmascript/module/tests/module_snapshot_test.cpp b/ecmascript/module/tests/module_snapshot_test.cpp index 4fd9a00eb3..7a3ba2de91 100644 --- a/ecmascript/module/tests/module_snapshot_test.cpp +++ b/ecmascript/module/tests/module_snapshot_test.cpp @@ -253,8 +253,8 @@ 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 +387,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/module_test/import_shared.js b/ecmascript/module/tests/module_test/import_shared.js new file mode 100644 index 0000000000..0027a8374d --- /dev/null +++ b/ecmascript/module/tests/module_test/import_shared.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/module_test/single_instance.js b/ecmascript/module/tests/module_test/single_instance.js new file mode 100644 index 0000000000..dfa73b64fe --- /dev/null +++ b/ecmascript/module/tests/module_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 './module_unexecute' \ No newline at end of file diff --git a/ecmascript/module/tests/module_test/single_instance_test.js b/ecmascript/module/tests/module_test/single_instance_test.js new file mode 100644 index 0000000000..cbfb7fb76f --- /dev/null +++ b/ecmascript/module/tests/module_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/module_test/single_instance_test_shared.js b/ecmascript/module/tests/module_test/single_instance_test_shared.js new file mode 100644 index 0000000000..99460f1f08 --- /dev/null +++ b/ecmascript/module/tests/module_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/entry.js b/ecmascript/module/tests/shared_test/entry.js new file mode 100644 index 0000000000..35beb7c407 --- /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 0000000000..84f46ebe7a --- /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 0000000000..83a76596b1 --- /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 0000000000..cbfb7fb76f --- /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 0000000000..99460f1f08 --- /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 0000000000..6bc69247f6 --- /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 5dd40211a3..f8767a5c94 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) -- Gitee