| @@ -23,6 +23,7 @@ |
| #include "ecmascript/jspandafile/js_pandafile.h" | #include "ecmascript/jspandafile/js_pandafile.h" |
| #include "ecmascript/jspandafile/js_pandafile_executor.h" | #include "ecmascript/jspandafile/js_pandafile_executor.h" |
| #include "ecmascript/jspandafile/js_pandafile_manager.h" | #include "ecmascript/jspandafile/js_pandafile_manager.h" |
| +#include "ecmascript/snapshot/common/modules_snapshot_helper.h" |
| #include "ecmascript/jspandafile/program_object.h" | #include "ecmascript/jspandafile/program_object.h" |
| #include "ecmascript/js_object-inl.h" | #include "ecmascript/js_object-inl.h" |
| #include "ecmascript/module/js_module_manager.h" | #include "ecmascript/module/js_module_manager.h" |
| @@ -105,15 +106,20 @@ public: |
| | |
| | |
| | |
| - static void ClearInstantiationFieldsIfNeeded(JSThread *thread, JSHandle<SourceTextModule> &module) | + static void ClearImportEntriesIfNeeded(JSThread *thread, JSHandle<SourceTextModule> &module) |
| { | { |
| - SourceTextModule::ClearInstantiationFieldsIfNeeded(thread, module); | + SourceTextModule::ClearImportEntriesIfNeeded(thread, module); |
| } | } |
| | |
| static void ClearTopLevelCapabilityIfNeeded(JSThread *thread, const JSHandle<SourceTextModule> &module) | static void ClearTopLevelCapabilityIfNeeded(JSThread *thread, const JSHandle<SourceTextModule> &module) |
| { | { |
| SourceTextModule::ClearTopLevelCapabilityIfNeeded(thread, module); | SourceTextModule::ClearTopLevelCapabilityIfNeeded(thread, module); |
| } | } |
| + |
| + static void ClearModuleRequestsIfNeeded(JSThread *thread, const JSHandle<SourceTextModule> &module) |
| + { |
| + SourceTextModule::ClearModuleRequestsIfNeeded(thread, module); |
| + } |
| }; | }; |
| | |
| JSHandle<JSTaggedValue> EcmaModuleTest::MockRequireNapiFailure(JsiRuntimeCallInfo *runtimeCallInfo) | JSHandle<JSTaggedValue> EcmaModuleTest::MockRequireNapiFailure(JsiRuntimeCallInfo *runtimeCallInfo) |
| @@ -7107,13 +7113,13 @@ HWTEST_F_L0(EcmaModuleTest, ModuleValueAccessor_GetNativeOrCjsExports_OtherModul |
| | |
| |
| * Feature: Module Memory Optimization | * Feature: Module Memory Optimization |
| - * Function: ClearInstantiationFieldsIfNeeded | + * Function: ClearImportEntriesIfNeeded |
| * SubFunction: ImportEntries cleared after Instantiate | * SubFunction: ImportEntries cleared after Instantiate |
| * FunctionPoints: Verify ImportEntries is set to Undefined | * FunctionPoints: Verify ImportEntries is set to Undefined |
| * CaseDescription: Create a module at INSTANTIATED status, add ImportEntries, call cleanup, | * CaseDescription: Create a module at INSTANTIATED status, add ImportEntries, call cleanup, |
| * verify ImportEntries is cleared | * verify ImportEntries is cleared |
| */ | */ |
| -HWTEST_F_L0(EcmaModuleTest, ClearInstantiationFields_ImportEntries_Cleared) | +HWTEST_F_L0(EcmaModuleTest, ClearImportEntries_ImportEntries_Cleared) |
| { | { |
| ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); | ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); |
| JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); | JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| @@ -7124,18 +7130,18 @@ HWTEST_F_L0(EcmaModuleTest, ClearInstantiationFields_ImportEntries_Cleared) |
| SourceTextModule::AddImportEntry(thread, module, importEntry, 0, 1); | SourceTextModule::AddImportEntry(thread, module, importEntry, 0, 1); |
| EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); | EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); |
| | |
| - ClearInstantiationFieldsIfNeeded(thread, module); | + ClearImportEntriesIfNeeded(thread, module); |
| EXPECT_TRUE(module->GetImportEntries(thread).IsUndefined()); | EXPECT_TRUE(module->GetImportEntries(thread).IsUndefined()); |
| } | } |
| | |
| |
| * Feature: Module Memory Optimization | * Feature: Module Memory Optimization |
| - * Function: ClearInstantiationFieldsIfNeeded | + * Function: ClearImportEntriesIfNeeded |
| - * SubFunction: ModuleRequests NOT cleared for shared module | + * SubFunction: ImportEntries NOT cleared for shared module |
| - * FunctionPoints: Verify ModuleRequests is preserved for shared modules | + * FunctionPoints: Verify ImportEntries is preserved for shared modules |
| - * CaseDescription: Shared module needs ModuleRequests for cross-thread fallback resolve | + * CaseDescription: Shared module ImportEntries should not be cleared by ClearImportEntriesIfNeeded |
| */ | */ |
| -HWTEST_F_L0(EcmaModuleTest, ClearInstantiationFields_ModuleRequests_NotCleared_Shared) | +HWTEST_F_L0(EcmaModuleTest, ClearImportEntries_NotCleared_Shared) |
| { | { |
| ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); | ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); |
| JSHandle<SourceTextModule> module = factory->NewSSourceTextModule(); | JSHandle<SourceTextModule> module = factory->NewSSourceTextModule(); |
| @@ -7143,23 +7149,24 @@ HWTEST_F_L0(EcmaModuleTest, ClearInstantiationFields_ModuleRequests_NotCleared_S |
| module->SetSharedType(SharedTypes::SHARED_MODULE); | module->SetSharedType(SharedTypes::SHARED_MODULE); |
| | |
| | |
| - // (WriteBarrier ASSERT in debug mode). ModuleRequests is initially undefined | + // (WriteBarrier ASSERT in debug mode). ImportEntries is initially undefined |
| - // and should remain undefined after cleanup — i.e., shared modules are skipped. | + // and should remain undefined — ClearImportEntriesIfNeeded does not have |
| - EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); | + // IsSharedModule check, but shared module ImportEntries starts as undefined. |
| + EXPECT_TRUE(module->GetImportEntries(thread).IsUndefined()); |
| | |
| - ClearInstantiationFieldsIfNeeded(thread, module); | + ClearImportEntriesIfNeeded(thread, module); |
| - EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); | + EXPECT_TRUE(module->GetImportEntries(thread).IsUndefined()); |
| } | } |
| | |
| |
| * Feature: Module Memory Optimization | * Feature: Module Memory Optimization |
| - * Function: ClearInstantiationFieldsIfNeeded | + * Function: ClearImportEntriesIfNeeded |
| * SubFunction: ImportEntries preserved when ModuleLogger is enabled | * SubFunction: ImportEntries preserved when ModuleLogger is enabled |
| * FunctionPoints: Verify ImportEntries is NOT cleared when ModuleLogger is active | * FunctionPoints: Verify ImportEntries is NOT cleared when ModuleLogger is active |
| * CaseDescription: ModuleLogger reads ImportEntries for diagnostics, so it must be | * CaseDescription: ModuleLogger reads ImportEntries for diagnostics, so it must be |
| * preserved even after Instantiate when logging is on. | * preserved even after Instantiate when logging is on. |
| */ | */ |
| -HWTEST_F_L0(EcmaModuleTest, ClearInstantiationFields_ImportEntries_Preserved_WithModuleLogger) | +HWTEST_F_L0(EcmaModuleTest, ClearImportEntries_ImportEntries_Preserved_WithModuleLogger) |
| { | { |
| EcmaVM *vm = thread->GetEcmaVM(); | EcmaVM *vm = thread->GetEcmaVM(); |
| ObjectFactory *factory = vm->GetFactory(); | ObjectFactory *factory = vm->GetFactory(); |
| @@ -7175,7 +7182,7 @@ HWTEST_F_L0(EcmaModuleTest, ClearInstantiationFields_ImportEntries_Preserved_Wit |
| ModuleLogger *moduleLogger = new ModuleLogger(vm); | ModuleLogger *moduleLogger = new ModuleLogger(vm); |
| thread->SetModuleLogger(moduleLogger); | thread->SetModuleLogger(moduleLogger); |
| | |
| - ClearInstantiationFieldsIfNeeded(thread, module); | + ClearImportEntriesIfNeeded(thread, module); |
| EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); | EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); |
| | |
| | |
| @@ -7190,7 +7197,7 @@ HWTEST_F_L0(EcmaModuleTest, ClearInstantiationFields_ImportEntries_Preserved_Wit |
| * FunctionPoints: Verify TopLevelCapability is set to Undefined after Evaluate | * FunctionPoints: Verify TopLevelCapability is set to Undefined after Evaluate |
| * CaseDescription: Non-shared module at EVALUATED status, TopLevelCapability should be cleared | * CaseDescription: Non-shared module at EVALUATED status, TopLevelCapability should be cleared |
| */ | */ |
| -HWTEST_F_L0(EcmaModuleTest, ClearPostEvaluationFields_TopLevelCapability_Cleared) | +HWTEST_F_L0(EcmaModuleTest, ClearTopLevelCapability_Cleared) |
| { | { |
| ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); | ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); |
| JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); | JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| @@ -7212,7 +7219,7 @@ HWTEST_F_L0(EcmaModuleTest, ClearPostEvaluationFields_TopLevelCapability_Cleared |
| * FunctionPoints: Verify shared module preserves TopLevelCapability | * FunctionPoints: Verify shared module preserves TopLevelCapability |
| * CaseDescription: Shared module needs TopLevelCapability for cross-thread Evaluate redirect | * CaseDescription: Shared module needs TopLevelCapability for cross-thread Evaluate redirect |
| */ | */ |
| -HWTEST_F_L0(EcmaModuleTest, ClearPostEvaluationFields_TopLevelCapability_NotCleared_Shared) | +HWTEST_F_L0(EcmaModuleTest, ClearTopLevelCapability_NotCleared_Shared) |
| { | { |
| ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); | ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); |
| JSHandle<SourceTextModule> module = factory->NewSSourceTextModule(); | JSHandle<SourceTextModule> module = factory->NewSSourceTextModule(); |
| @@ -7228,6 +7235,68 @@ HWTEST_F_L0(EcmaModuleTest, ClearPostEvaluationFields_TopLevelCapability_NotClea |
| EXPECT_TRUE(module->GetTopLevelCapability(thread).IsUndefined()); | EXPECT_TRUE(module->GetTopLevelCapability(thread).IsUndefined()); |
| } | } |
| | |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearModuleRequestsIfNeeded |
| + * SubFunction: ModuleRequests NOT cleared when VM state is CANNOT_CLEAR |
| + * FunctionPoints: Verify ModuleRequests is preserved when snapshot is active |
| + * CaseDescription: Non-shared module, snapshot active (CANNOT_CLEAR state), |
| + * ModuleRequests should not be cleared until serialization completes |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_NotCleared_CannotClearState) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + JSHandle<EcmaString> requestStr = factory->NewFromASCII("./module_a"); |
| + JSHandle<TaggedArray> moduleRequests = factory->NewTaggedArray(1); |
| + moduleRequests->Set(thread, 0, requestStr); |
| + module->SetModuleRequests(thread, moduleRequests); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CANNOT_CLEAR); |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CAN_CLEAR); |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearModuleRequestsIfNeeded |
| + * SubFunction: ModuleRequests NOT cleared for shared module regardless of VM state |
| + * FunctionPoints: Verify shared modules skip ModuleRequests clearing entirely |
| + * CaseDescription: Shared module needs ModuleRequests for cross-thread fallback resolve |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_NotCleared_Shared) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::SHARED_MODULE); |
| + |
| + |
| + |
| + |
| + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CAN_CLEAR); |
| + |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| +} |
| + |
| |
| * Feature: Module Memory Optimization | * Feature: Module Memory Optimization |
| * Function: InnerModuleEvaluationUnsafe line 1059 guard | * Function: InnerModuleEvaluationUnsafe line 1059 guard |
| @@ -7251,14 +7320,17 @@ HWTEST_F_L0(EcmaModuleTest, EvaluatedModule_SkipsCycleRootErrorCheck) |
| | |
| |
| * Feature: Module Memory Optimization | * Feature: Module Memory Optimization |
| - * Function: ClearInstantiationFieldsIfNeeded + ClearTopLevelCapabilityIfNeeded | + * Function: ClearImportEntriesIfNeeded + ClearTopLevelCapabilityIfNeeded |
| * SubFunction: Full cleanup lifecycle for non-shared module | * SubFunction: Full cleanup lifecycle for non-shared module |
| - * FunctionPoints: Verify both cleanup phases work correctly | + * FunctionPoints: Verify both sync and async cleanup phases work correctly with VM state |
| - * CaseDescription: Instantiate phase clears ImportEntries, Evaluate phase clears TopLevelCapability | + * CaseDescription: Sync path (InnerModuleEvaluationUnsafe): ImportEntries + TopLevelCapability |
| + * Async path (AsyncModuleExecutionFulfilled): TopLevelCapability + ModuleRequests |
| + * ModuleRequests clearing depends on VM ModuleRequestsClearState |
| */ | */ |
| HWTEST_F_L0(EcmaModuleTest, FullLifecycle_NonSharedModule_FieldsCleared) | HWTEST_F_L0(EcmaModuleTest, FullLifecycle_NonSharedModule_FieldsCleared) |
| { | { |
| - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); | + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); | JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); | module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| | |
| @@ -7266,75 +7338,310 @@ HWTEST_F_L0(EcmaModuleTest, FullLifecycle_NonSharedModule_FieldsCleared) |
| module->SetStatus(ModuleStatus::INSTANTIATED); | module->SetStatus(ModuleStatus::INSTANTIATED); |
| JSHandle<ImportEntry> importEntry = factory->NewImportEntry(); | JSHandle<ImportEntry> importEntry = factory->NewImportEntry(); |
| SourceTextModule::AddImportEntry(thread, module, importEntry, 0, 1); | SourceTextModule::AddImportEntry(thread, module, importEntry, 0, 1); |
| + JSHandle<EcmaString> requestStr = factory->NewFromASCII("./module_a"); |
| + JSHandle<TaggedArray> moduleRequests = factory->NewTaggedArray(1); |
| + moduleRequests->Set(thread, 0, requestStr); |
| + module->SetModuleRequests(thread, moduleRequests); |
| EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); | EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| | |
| - ClearInstantiationFieldsIfNeeded(thread, module); | + ClearImportEntriesIfNeeded(thread, module); |
| EXPECT_TRUE(module->GetImportEntries(thread).IsUndefined()); | EXPECT_TRUE(module->GetImportEntries(thread).IsUndefined()); |
| + |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| | |
| - // Phase 2: Evaluate → clear TopLevelCapability | + // Phase 2: Evaluate — sync path (InnerModuleEvaluationUnsafe) |
| + |
| module->SetStatus(ModuleStatus::EVALUATED); | module->SetStatus(ModuleStatus::EVALUATED); |
| JSHandle<JSPromise> capability = factory->NewJSPromise(); | JSHandle<JSPromise> capability = factory->NewJSPromise(); |
| module->SetTopLevelCapability(thread, capability); | module->SetTopLevelCapability(thread, capability); |
| EXPECT_FALSE(module->GetTopLevelCapability(thread).IsUndefined()); | EXPECT_FALSE(module->GetTopLevelCapability(thread).IsUndefined()); |
| | |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CANNOT_CLEAR); |
| + ClearTopLevelCapabilityIfNeeded(thread, module); |
| + EXPECT_TRUE(module->GetTopLevelCapability(thread).IsUndefined()); |
| + |
| + |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CAN_CLEAR); |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearModuleRequestsIfNeeded |
| + * SubFunction: UNINITIALIZED lazy init — DisableModuleSnapshot sets CAN_CLEAR |
| + * FunctionPoints: Verify lazy init path evaluates DisableModuleSnapshot and caches state |
| + * CaseDescription: VM state is UNINITIALIZED, DisableModuleSnapshot is true, |
| + * first call should init state to CAN_CLEAR and clear ModuleRequests |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_LazyInit_DisableModuleSnapshot) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + JSHandle<EcmaString> requestStr = factory->NewFromASCII("./module_a"); |
| + JSHandle<TaggedArray> moduleRequests = factory->NewTaggedArray(1); |
| + moduleRequests->Set(thread, 0, requestStr); |
| + module->SetModuleRequests(thread, moduleRequests); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_NE(vm->GetModuleRequestsClearState(), ModuleRequestsClearState::UNINITIALIZED); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearModuleRequestsIfNeeded |
| + * SubFunction: IsUndefined early return skips redundant clearing |
| + * FunctionPoints: Verify already-cleared module is skipped without side effects |
| + * CaseDescription: ModuleRequests already undefined, second call should return immediately |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_SkipAlreadyCleared) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + |
| + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CAN_CLEAR); |
| + |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearModuleRequestsIfNeeded |
| + * SubFunction: UNINITIALIZED lazy init with g_featureLoaded_ MODULE bit set |
| + * FunctionPoints: Verify lazy init path checks g_featureLoaded_ and sets CAN_CLEAR |
| + * CaseDescription: VM state UNINITIALIZED, g_featureLoaded_ MODULE bit set |
| + * (simulating second launch after serialization), should clear ModuleRequests |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_LazyInit_FeatureLoaded) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + JSHandle<EcmaString> requestStr = factory->NewFromASCII("./module_a"); |
| + JSHandle<TaggedArray> moduleRequests = factory->NewTaggedArray(1); |
| + moduleRequests->Set(thread, 0, requestStr); |
| + module->SetModuleRequests(thread, moduleRequests); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->GetJSOptions().SetDisableModuleSnapshot(true); |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| + ModulesSnapshotHelper::MarkModuleSnapshotLoaded(); |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_EQ(vm->GetModuleRequestsClearState(), ModuleRequestsClearState::CAN_CLEAR); |
| + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| + vm->GetJSOptions().SetDisableModuleSnapshot(false); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearImportEntriesIfNeeded |
| + * SubFunction: ImportEntries preserved when IsDebugApp is true |
| + * FunctionPoints: Verify ImportEntries is NOT cleared when debugger is attached |
| + * CaseDescription: Debug app needs ImportEntries for debugging, so it must be preserved. |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearImportEntries_NotCleared_DebugApp) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::INSTANTIATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + JSHandle<ImportEntry> importEntry = factory->NewImportEntry(); |
| + SourceTextModule::AddImportEntry(thread, module, importEntry, 0, 1); |
| + EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); |
| + |
| + vm->GetJsDebuggerManager()->SetIsDebugApp(true); |
| + ClearImportEntriesIfNeeded(thread, module); |
| + EXPECT_FALSE(module->GetImportEntries(thread).IsUndefined()); |
| + |
| + |
| + vm->GetJsDebuggerManager()->SetIsDebugApp(false); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearTopLevelCapabilityIfNeeded |
| + * SubFunction: TopLevelCapability preserved when IsDebugApp is true |
| + * FunctionPoints: Verify TopLevelCapability is NOT cleared when debugger is attached |
| + * CaseDescription: Debug app needs TopLevelCapability for debugging, so it must be preserved. |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearTopLevelCapability_NotCleared_DebugApp) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + JSHandle<JSPromise> capability = factory->NewJSPromise(); |
| + module->SetTopLevelCapability(thread, capability); |
| + EXPECT_FALSE(module->GetTopLevelCapability(thread).IsUndefined()); |
| + |
| + vm->GetJsDebuggerManager()->SetIsDebugApp(true); |
| + ClearTopLevelCapabilityIfNeeded(thread, module); |
| + EXPECT_FALSE(module->GetTopLevelCapability(thread).IsUndefined()); |
| + |
| + |
| + vm->GetJsDebuggerManager()->SetIsDebugApp(false); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearModuleRequestsIfNeeded |
| + * SubFunction: ModuleRequests preserved when IsDebugApp is true |
| + * FunctionPoints: Verify ModuleRequests is NOT cleared when debugger is attached |
| + * CaseDescription: Debug app needs ModuleRequests for debugging, so it must be preserved. |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_NotCleared_DebugApp) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + JSHandle<EcmaString> requestStr = factory->NewFromASCII("./module_a"); |
| + JSHandle<TaggedArray> moduleRequests = factory->NewTaggedArray(1); |
| + moduleRequests->Set(thread, 0, requestStr); |
| + module->SetModuleRequests(thread, moduleRequests); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CAN_CLEAR); |
| + vm->GetJsDebuggerManager()->SetIsDebugApp(true); |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + vm->GetJsDebuggerManager()->SetIsDebugApp(false); |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| +} |
| + |
| + |
| + * Feature: Module Memory Optimization |
| + * Function: ClearTopLevelCapabilityIfNeeded |
| + * SubFunction: TopLevelCapability IsUndefined early return |
| + * FunctionPoints: Verify already-undefined TopLevelCapability is skipped without side effects |
| + * CaseDescription: TopLevelCapability already undefined, call should return immediately. |
| + */ |
| +HWTEST_F_L0(EcmaModuleTest, ClearTopLevelCapability_SkipAlreadyUndefined) |
| +{ |
| + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| + JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| + module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| + |
| + |
| + EXPECT_TRUE(module->GetTopLevelCapability(thread).IsUndefined()); |
| + |
| ClearTopLevelCapabilityIfNeeded(thread, module); | ClearTopLevelCapabilityIfNeeded(thread, module); |
| EXPECT_TRUE(module->GetTopLevelCapability(thread).IsUndefined()); | EXPECT_TRUE(module->GetTopLevelCapability(thread).IsUndefined()); |
| } | } |
| | |
| |
| * Feature: Module Memory Optimization | * Feature: Module Memory Optimization |
| - * Function: SerializeModuleCNativeObjects | + * Function: ClearModuleRequestsIfNeeded |
| - * SubFunction: ModuleRequests=Undefined → serialization skips gracefully | + * SubFunction: ModuleRequests NOT cleared when LazyImportStatusArray exists |
| - * FunctionPoints: Verify no crash when serializing module with cleaned ModuleRequests | + * FunctionPoints: Verify ModuleRequests is preserved when lazyArray is non-null |
| - * CaseDescription: After ClearInstantiationFieldsIfNeeded, ModuleRequests is Undefined. | + * CaseDescription: Serialization depends on lazyArray && ModuleRequests.IsTaggedArray(), |
| - * Serialization should skip lazy-import array traversal safely. | + * so ModuleRequests must not be cleared while lazyArray exists. |
| */ | */ |
| -HWTEST_F_L0(EcmaModuleTest, ModuleRequestsUndefined_SnapshotSerializeNoCrash) | +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_NotCleared_LazyArrayExists) |
| { | { |
| - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); | + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); | JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| - module->SetEcmaModuleRecordNameString("test"); | + module->SetStatus(ModuleStatus::EVALUATED); |
| module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); | module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| | |
| - // Simulate cleaned ModuleRequests after ClearInstantiationFieldsIfNeeded | + JSHandle<EcmaString> requestStr = factory->NewFromASCII("./module_a"); |
| - module->SetModuleRequests(thread, JSTaggedValue::Undefined()); | + JSHandle<TaggedArray> moduleRequests = factory->NewTaggedArray(1); |
| + moduleRequests->Set(thread, 0, requestStr); |
| + module->SetModuleRequests(thread, moduleRequests); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| | |
| - // Verify serialization code can read Undefined ModuleRequests without crash: | + // Simulate lazy import array present |
| - // lazyArray && requests.IsTaggedArray() → false → skip traversal | + bool *lazyArray = new bool[1] {false}; |
| - JSTaggedValue requests = module->GetModuleRequests(thread); | + module->SetLazyImportArray(lazyArray); |
| - EXPECT_TRUE(requests.IsUndefined()); | + |
| - EXPECT_FALSE(requests.IsTaggedArray()); | + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CAN_CLEAR); |
| + ClearModuleRequestsIfNeeded(thread, module); |
| + |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| + |
| + |
| + module->DestroyLazyImportArray(); |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| } | } |
| | |
| |
| * Feature: Module Memory Optimization | * Feature: Module Memory Optimization |
| - * Function: ClearInstantiationFieldsIfNeeded | + * Function: ClearModuleRequestsIfNeeded |
| - * SubFunction: Module variable access via Environment unaffected by ModuleRequests cleanup | + * SubFunction: ModuleRequests cleared when LazyImportStatusArray is null |
| - * FunctionPoints: Verify Environment binding is preserved after ModuleRequests is cleared | + * FunctionPoints: Verify ModuleRequests is cleared when lazyArray is null and VM state allows |
| - * CaseDescription: Module variable access goes through Environment, not ModuleRequests. | + * CaseDescription: When lazyArray is null, serialization does not depend on ModuleRequests, |
| - * Clearing ModuleRequests after Instantiate must not affect variable access. | + * so it can be safely cleared. |
| */ | */ |
| -HWTEST_F_L0(EcmaModuleTest, ModuleVariableAccess_AfterModuleRequestsCleared) | +HWTEST_F_L0(EcmaModuleTest, ClearModuleRequests_Cleared_LazyArrayNull) |
| { | { |
| - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); | + EcmaVM *vm = thread->GetEcmaVM(); |
| + ObjectFactory *factory = vm->GetFactory(); |
| JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); | JSHandle<SourceTextModule> module = factory->NewSourceTextModule(); |
| - module->SetEcmaModuleRecordNameString("test"); | |
| - module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); | |
| module->SetStatus(ModuleStatus::EVALUATED); | module->SetStatus(ModuleStatus::EVALUATED); |
| + module->SetSharedType(SharedTypes::UNSENDABLE_MODULE); |
| | |
| - // Set up Environment binding (simulates Instantiate result) | + JSHandle<EcmaString> requestStr = factory->NewFromASCII("./module_a"); |
| - JSHandle<TaggedArray> environment = factory->NewTaggedArray(1); | + JSHandle<TaggedArray> moduleRequests = factory->NewTaggedArray(1); |
| - environment->Set(thread, 0, JSTaggedValue(42)); | + moduleRequests->Set(thread, 0, requestStr); |
| - module->SetEnvironment(thread, environment); | + module->SetModuleRequests(thread, moduleRequests); |
| + EXPECT_FALSE(module->GetModuleRequests(thread).IsUndefined()); |
| | |
| - // Clear ModuleRequests | + // lazyArray is null by default (NewSourceTextModule does not set it) |
| - module->SetModuleRequests(thread, JSTaggedValue::Undefined()); | + EXPECT_EQ(module->GetLazyImportStatusArray(), nullptr); |
| | |
| - // Environment still accessible | + vm->SetModuleRequestsClearState(ModuleRequestsClearState::CAN_CLEAR); |
| - JSTaggedValue envValue = module->GetEnvironment(thread); | + ClearModuleRequestsIfNeeded(thread, module); |
| - EXPECT_FALSE(envValue.IsUndefined()); | + EXPECT_TRUE(module->GetModuleRequests(thread).IsUndefined()); |
| - JSHandle<TaggedArray> env(thread, envValue); | + |
| - EXPECT_EQ(env->Get(thread, 0), JSTaggedValue(42)); | + // Cleanup |
| + vm->SetModuleRequestsClearState(ModuleRequestsClearState::UNINITIALIZED); |
| } | } |
| | |
| |
| |