diff --git a/ecmascript/jspandafile/js_pandafile_executor.cpp b/ecmascript/jspandafile/js_pandafile_executor.cpp index a5c31ef300d8dd2faceb448294148e107a5fedca..28ac7561d3623974a71074178b48b9cb76b1d59d 100644 --- a/ecmascript/jspandafile/js_pandafile_executor.cpp +++ b/ecmascript/jspandafile/js_pandafile_executor.cpp @@ -66,7 +66,9 @@ Expected JSPandaFileExecutor::ExecuteFromFile(JSThread *thr SourceTextModule::Instantiate(thread, moduleRecord, executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, Unexpected(false)); JSHandle module = JSHandle::Cast(moduleRecord); - module->SetStatus(ModuleStatus::INSTANTIATED); + if (!SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module)) { + return JSTaggedValue::Undefined(); + } SourceTextModule::Evaluate(thread, module, nullptr, 0, executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, Unexpected(false)); return JSTaggedValue::Undefined(); @@ -205,7 +207,9 @@ Expected JSPandaFileExecutor::CommonExecuteBuffer(JSThread RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, Unexpected(false)); JSHandle module = JSHandle::Cast(moduleRecord); - module->SetStatus(ModuleStatus::INSTANTIATED); + if (!SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module)) { + return JSTaggedValue::Undefined(); + } SourceTextModule::Evaluate(thread, module, buffer, size, executeType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, Unexpected(false)); return JSTaggedValue::Undefined(); @@ -309,7 +313,9 @@ Expected JSPandaFileExecutor::CommonExecuteBuffer(JSThread SourceTextModule::Instantiate(thread, moduleRecord); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, Unexpected(false)); JSHandle module = JSHandle::Cast(moduleRecord); - module->SetStatus(ModuleStatus::INSTANTIATED); + if (!SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module)) { + return JSTaggedValue::Undefined(); + } //After the module is instantiated, stop preloading so and parallel abc loading thread->GetEcmaVM()->StopPreLoadSoOrAbc(); @@ -499,7 +505,7 @@ int JSPandaFileExecutor::ExecuteAbcFileWithSingletonPatternFlag(JSThread *thread SourceTextModule::Instantiate(thread, moduleRecord); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ROUTE_INTERNAL_ERROR); JSHandle module = JSHandle::Cast(moduleRecord); - if (!isSingletonPattern) { + if (!isSingletonPattern && !SourceTextModule::IsSharedModule(module)) { LOG_ECMA(INFO) << "Route jump to non-singleton page: " << entryPoint; module->SetStatus(ModuleStatus::INSTANTIATED); } else { diff --git a/ecmascript/module/js_module_source_text.cpp b/ecmascript/module/js_module_source_text.cpp index b69265c07cf7ea3726712d4ccd17a9c20dda3d4e..ff931f8ae4ae43f2529fc7f42fd7fcb5a5a32ed6 100644 --- a/ecmascript/module/js_module_source_text.cpp +++ b/ecmascript/module/js_module_source_text.cpp @@ -2332,4 +2332,22 @@ JSHandle SourceTextModule::FindFuncInModuleForHook(JSThread* thre } return functionFound; } + +/* + * normal module: will re-execute once interface is called. + * shared module: only execute once. + */ +bool SourceTextModule::ResetStatusForNormalModuleToReExecute(JSThread* thread, JSHandle module) +{ + if (!SourceTextModule::IsSharedModule(module)) { + module->SetStatus(ModuleStatus::INSTANTIATED); + return true; + } + LOG_ECMA(ERROR) << "Shared Module: " << module->GetEcmaModuleRecordNameString() << " loading is not supported."; + if (module->GetStatus() >= ModuleStatus::EVALUATED) { + return false; + } + // Shared module in evaluating status will still undergo evaluate process to ensure module execution completion. + return true; +} } // namespace panda::ecmascript diff --git a/ecmascript/module/js_module_source_text.h b/ecmascript/module/js_module_source_text.h index 566aed9616eed9ba305306fd98883968315e547f..af7e6a8c90f8b0934f4a3026aa4f5bea35e4fc43 100644 --- a/ecmascript/module/js_module_source_text.h +++ b/ecmascript/module/js_module_source_text.h @@ -440,6 +440,8 @@ public: // Find function in JsModuleSourceText For Hook static JSHandle FindFuncInModuleForHook(JSThread* thread, const std::string &recordName, const std::string &className, const std::string &funcName); + + static bool ResetStatusForNormalModuleToReExecute(JSThread* thread, JSHandle module); private: static JSHandle GetStarResolution(JSThread *thread, diff --git a/ecmascript/module/tests/BUILD.gn b/ecmascript/module/tests/BUILD.gn index 5bd309b271b7750545a87e41eb17dbcdc428cff5..0b44205eba35e11bad9f74c4f69dc82bd3a7824c 100644 --- a/ecmascript/module/tests/BUILD.gn +++ b/ecmascript/module/tests/BUILD.gn @@ -26,6 +26,9 @@ test_js_files = [ "module_unexecute_A", "module_unexecute_B", "module_unexecute_C", + "single_instance_test_shared", + "single_instance_test", + "single_instance" ] foreach(file, test_js_files) { diff --git a/ecmascript/module/tests/ecma_module_test.cpp b/ecmascript/module/tests/ecma_module_test.cpp index 05fe497b11d7edeb7cf4d720d7e166a7feb77977..7458481eb89fa030472ea49c134f8ccce3c92426 100644 --- a/ecmascript/module/tests/ecma_module_test.cpp +++ b/ecmascript/module/tests/ecma_module_test.cpp @@ -37,6 +37,7 @@ #include "ecmascript/module/module_path_helper.h" #include "ecmascript/module/module_resolver.h" #include "ecmascript/module/module_tools.h" +#include "ecmascript/jspandafile/js_pandafile_executor.h" #include "ecmascript/require/js_cjs_module.h" #include "ecmascript/tests/test_helper.h" #include "libpandafile/class_data_accessor-inl.h" @@ -4288,4 +4289,79 @@ HWTEST_F_L0(EcmaModuleTest, UpdateSharedModule) thread, curmodule, resolvedBinding.GetTaggedValue(), false); EXPECT_EQ(res, val.GetTaggedValue()); } + +HWTEST_F_L0(EcmaModuleTest, ResetStatusForNormalModuleToReExecute) +{ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); + // input is normal module status == instantiaed + JSHandle module1 = objectFactory->NewSourceTextModule(); + module1->SetSharedType(SharedTypes::UNSENDABLE_MODULE); + module1->SetStatus(ModuleStatus::INSTANTIATED); + bool res1 = SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module1); + EXPECT_TRUE(res1); + // input is normal module status > instantiaed + JSHandle module2 = objectFactory->NewSourceTextModule(); + module2->SetSharedType(SharedTypes::UNSENDABLE_MODULE); + module2->SetStatus(ModuleStatus::EVALUATED); + bool res2 = SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module2); + EXPECT_TRUE(res2); + // input is shared module status == instantiaed + JSHandle module3 = objectFactory->NewSSourceTextModule(); + module3->SetSharedType(SharedTypes::SHARED_MODULE); + module3->SetStatus(ModuleStatus::INSTANTIATED); + bool res3 = SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module3); + EXPECT_TRUE(res3); + // input is shared module status == evaluating + JSHandle module4 = objectFactory->NewSSourceTextModule(); + module4->SetSharedType(SharedTypes::SHARED_MODULE); + module4->SetStatus(ModuleStatus::EVALUATING); + bool res4 = SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module4); + EXPECT_TRUE(res4); + // input is shared module status == evaluated + JSHandle module5 = objectFactory->NewSSourceTextModule(); + module5->SetSharedType(SharedTypes::SHARED_MODULE); + module5->SetStatus(ModuleStatus::EVALUATED); + bool res5 = SourceTextModule::ResetStatusForNormalModuleToReExecute(thread, module5); + EXPECT_TRUE(!res5); +} + +HWTEST_F_L0(EcmaModuleTest, SharedModuleReExecuteTest) +{ + CString baseFileName = MODULE_ABC_PATH "single_instance_test_shared.abc"; + // shared module import first time + Expected result1 = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, "single_instance_test_shared"); + EXPECT_TRUE(result1); + ModuleManager *moduleManager = thread->GetModuleManager(); + JSHandle module = moduleManager->GetImportedModule("single_instance_test_shared"); + JSHandle moduleRecord = JSHandle::Cast(module); + JSTaggedValue val1 = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord); + EXPECT_EQ(val1, JSTaggedValue(1)); + // shared module import second time + Expected result2 = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, "single_instance_test_shared"); + EXPECT_TRUE(result2); + JSTaggedValue val2 = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord); + EXPECT_EQ(val2, JSTaggedValue(1)); +} + +HWTEST_F_L0(EcmaModuleTest, NormalModuleReExecuteTest) +{ + CString baseFileName = MODULE_ABC_PATH "single_instance_test.abc"; + // normal module import first time + Expected result1 = + JSPandaFileExecutor::ExecuteFromFile(thread, baseFileName, "single_instance_test"); + EXPECT_TRUE(result1); + ModuleManager *moduleManager = thread->GetModuleManager(); + JSHandle module = moduleManager->GetImportedModule("single_instance_test"); + 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, "single_instance_test"); + EXPECT_TRUE(result2); + JSTaggedValue val2 = ModuleValueAccessor::GetModuleValueInner(thread, 0, moduleRecord); + EXPECT_EQ(val2, JSTaggedValue(2)); +} } // namespace panda::test 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 0000000000000000000000000000000000000000..029ba2ca0011bf4126c2d0616ae0a9d3aeb74549 --- /dev/null +++ b/ecmascript/module/tests/module_test/single_instance.js @@ -0,0 +1,22 @@ +/* + * 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}; \ 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 0000000000000000000000000000000000000000..cbfb7fb76f7682da73c95f9f4779c39f20a9b4b3 --- /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 0000000000000000000000000000000000000000..3a6cf2c312b62b7f056652a4bb01c98ed642195d --- /dev/null +++ b/ecmascript/module/tests/module_test/single_instance_test_shared.js @@ -0,0 +1,20 @@ +/* + * 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" +"use shared" +let instanceA = new A(); +instanceA.add(); +export let normalA = A.a; \ No newline at end of file