diff --git a/ecmascript/ecma_context.h b/ecmascript/ecma_context.h index 2ad4d05ee5..2c7f0144ea 100644 --- a/ecmascript/ecma_context.h +++ b/ecmascript/ecma_context.h @@ -124,6 +124,23 @@ public: bool ExecutePromisePendingJob(); + bool IsInPendingJob() const + { + return pendingJobEnterCount > 0; + } + + void AddPendingJobEnterCount() + { + ASSERT(pendingJobEnterCount < std::numeric_limits::max()); + ++pendingJobEnterCount; + } + + void MinusPendingJobEnterCount() + { + ASSERT(IsInPendingJob()); + --pendingJobEnterCount; + } + static EcmaContext *ConstCast(const EcmaContext *context) { return const_cast(context); @@ -780,6 +797,8 @@ private: bool hasKeptObjects_ {false}; + uint64_t pendingJobEnterCount {0}; + friend class EcmaHandleScope; friend class JSPandaFileExecutor; friend class ObjectFactory; diff --git a/ecmascript/jobs/micro_job_queue.cpp b/ecmascript/jobs/micro_job_queue.cpp index cdd3251b06..fbc142ece5 100644 --- a/ecmascript/jobs/micro_job_queue.cpp +++ b/ecmascript/jobs/micro_job_queue.cpp @@ -20,8 +20,19 @@ #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tagged_queue.h" +#include "ecmascript/ecma_context.h" namespace panda::ecmascript::job { +MicroJobScope::MicroJobScope(JSThread* thread) : thread_(thread) +{ + thread_->GetCurrentEcmaContext()->AddPendingJobEnterCount(); +} + +MicroJobScope::~MicroJobScope() +{ + thread_->GetCurrentEcmaContext()->MinusPendingJobEnterCount(); +} + uint32_t MicroJobQueue::GetPromiseQueueSize(JSThread *thread, JSHandle jobQueue) { [[maybe_unused]] EcmaHandleScope handleScope(thread); @@ -58,6 +69,7 @@ void MicroJobQueue::EnqueueJob(JSThread *thread, JSHandle jobQueu void MicroJobQueue::ExecutePendingJob(JSThread *thread, JSHandle jobQueue) { [[maybe_unused]] EcmaHandleScope handleScope(thread); + [[maybe_unused]] MicroJobScope microJobScope(thread); JSMutableHandle promiseQueue(thread, jobQueue->GetPromiseJobQueue()); JSMutableHandle pendingJob(thread, JSTaggedValue::Undefined()); while (!promiseQueue->Empty()) { diff --git a/ecmascript/jobs/micro_job_queue.h b/ecmascript/jobs/micro_job_queue.h index d6a60f5f34..7795657e45 100644 --- a/ecmascript/jobs/micro_job_queue.h +++ b/ecmascript/jobs/micro_job_queue.h @@ -23,7 +23,13 @@ #include "ecmascript/tagged_array.h" namespace panda::ecmascript::job { - +class MicroJobScope { +public: + explicit MicroJobScope(JSThread *thread); + ~MicroJobScope(); +private: + JSThread *thread_; +}; class MicroJobQueue final : public Record { public: static MicroJobQueue *Cast(TaggedObject *object) diff --git a/ecmascript/module/js_module_manager.cpp b/ecmascript/module/js_module_manager.cpp index 3359bd4549..71a5b237c3 100644 --- a/ecmascript/module/js_module_manager.cpp +++ b/ecmascript/module/js_module_manager.cpp @@ -181,7 +181,7 @@ JSTaggedValue ModuleManager::GetLazyModuleValueOutterInternal(int32_t index, JST if (resolvedBinding.IsResolvedIndexBinding()) { JSHandle binding(thread, resolvedBinding); JSTaggedValue resolvedModule = binding->GetModule(); - JSHandle module(thread, resolvedModule); + JSMutableHandle module(thread, resolvedModule); ASSERT(resolvedModule.IsSourceTextModule()); // Support for only modifying var value of HotReload. // Cause patchFile exclude the record of importing modifying var. Can't reresolve moduleRecord. @@ -191,13 +191,12 @@ JSTaggedValue ModuleManager::GetLazyModuleValueOutterInternal(int32_t index, JST context->FindPatchModule(module->GetEcmaModuleRecordNameString()); if (!resolvedModuleOfHotReload->IsHole()) { resolvedModule = resolvedModuleOfHotReload.GetTaggedValue(); - JSHandle moduleOfHotReload(thread, resolvedModule); - SourceTextModule::Evaluate(thread, moduleOfHotReload, nullptr); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); - return ModuleManagerHelper::GetModuleValue(thread, moduleOfHotReload, binding->GetIndex()); + module.Update(resolvedModule); } } - SourceTextModule::Evaluate(thread, module, nullptr); + if (module->GetStatus() != ModuleStatus::EVALUATED) { + SourceTextModule::Evaluate(thread, module, nullptr, 0, context->IsInPendingJob()); + } RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); return ModuleManagerHelper::GetModuleValue(thread, module, binding->GetIndex()); } diff --git a/test/moduletest/moduleLazyImport/asyncUseLazyImport.ts b/test/moduletest/moduleLazyImport/asyncUseLazyImport.ts new file mode 100644 index 0000000000..302aeda3b8 --- /dev/null +++ b/test/moduletest/moduleLazyImport/asyncUseLazyImport.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 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. + */ + +/* + * @tc.name:asyncUseLazyImport + * @tc.desc:test asyncUseLazyImport + * @tc.type: FUNC + * @tc.require: issue#IB2HNC + */ + +// @ts-nocheck +import lazy { TestLazy } from './lazyExport'; +export class Test { + async start() { + this.log(` Test start`) + let a = await this.mapAsync([1, 2], async (t) => { + await this.bindRawData(t) + }) + this.log(` Test end ${new Error().stack}`) + return a + } + private async bindRawData(t: number) { + await this.mapAsync([1, 2], async (k, b) => { + this.onBindRawData(t, k) + }) + await this.mapAsync([4, 5], async (k, b) => { + this.onBindRawData(t, k) + }) + await this.mapAsync([7, 8], async (k, b) => { + this.onBindRawData(t, k) + }) + } + async mapAsync(array: T[], map: (t: T, index: number) => Promise) { + return await Promise.all(array.map(map)) + } + private async onBindRawData(t: number, k: number) { + this.log(` onBindRawData start ${t} ${k}`) + await this.isSelf() + this.log(` onBindRawData end ${t} ${k}`) + } + private async isSelf(): Promise { + return "1" == "1" + } + log(msg: string) { + TestLazy + print(msg) + } +} \ No newline at end of file diff --git a/test/moduletest/moduleLazyImport/expect_output.txt b/test/moduletest/moduleLazyImport/expect_output.txt index 0c3a5d9c99..a6a589b5f2 100644 --- a/test/moduletest/moduleLazyImport/expect_output.txt +++ b/test/moduletest/moduleLazyImport/expect_output.txt @@ -30,3 +30,30 @@ this is dynamicLazySequence this is dynamicSubFile this is dynamicLazySequence2 dynamicSubFile + Test start + onBindRawData start 1 1 + onBindRawData start 1 2 + onBindRawData start 2 1 + onBindRawData start 2 2 + onBindRawData end 1 1 + onBindRawData end 1 2 + onBindRawData end 2 1 + onBindRawData end 2 2 + onBindRawData start 1 4 + onBindRawData start 1 5 + onBindRawData start 2 4 + onBindRawData start 2 5 + onBindRawData end 1 4 + onBindRawData end 1 5 + onBindRawData end 2 4 + onBindRawData end 2 5 + onBindRawData start 1 7 + onBindRawData start 1 8 + onBindRawData start 2 7 + onBindRawData start 2 8 + onBindRawData end 1 7 + onBindRawData end 1 8 + onBindRawData end 2 7 + onBindRawData end 2 8 + Test end at start (asyncUseLazyImport.ts:31:1) + diff --git a/test/moduletest/moduleLazyImport/lazyExport.js b/test/moduletest/moduleLazyImport/lazyExport.js new file mode 100644 index 0000000000..9302db1adb --- /dev/null +++ b/test/moduletest/moduleLazyImport/lazyExport.js @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 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. + */ + +/* + * @tc.name:asyncUseLazyImport + * @tc.desc:test asyncUseLazyImport + * @tc.type: FUNC + * @tc.require: issue#IB2HNC + */ + +// @ts-nocheck +export class TestLazy {} \ No newline at end of file diff --git a/test/moduletest/moduleLazyImport/moduleLazyImport.js b/test/moduletest/moduleLazyImport/moduleLazyImport.js index fc3bfa8725..667e9749ac 100644 --- a/test/moduletest/moduleLazyImport/moduleLazyImport.js +++ b/test/moduletest/moduleLazyImport/moduleLazyImport.js @@ -25,6 +25,7 @@ import { aaa } from "./E" import lazy {func1} from './F' import lazy {dynamicLazySequence} from './dynamicLazySequence' import lazy {dynamicLazySequence2} from './dynamicLazySequence2' +import {Test} from './asyncUseLazyImport' print("this is entry"); func1(); @@ -35,4 +36,5 @@ print(h); import("./dynamicLazySequence").then((ns) => { print(ns.dynamicLazySequence); }) -import("./dynamicLazySequence2"); \ No newline at end of file +import("./dynamicLazySequence2"); +new Test().start() \ No newline at end of file diff --git a/test/moduletest/moduleLazyImport/moduleLazyImport.txt b/test/moduletest/moduleLazyImport/moduleLazyImport.txt index ca0c06d82a..8fc9a7e6be 100644 --- a/test/moduletest/moduleLazyImport/moduleLazyImport.txt +++ b/test/moduletest/moduleLazyImport/moduleLazyImport.txt @@ -9,4 +9,6 @@ H.js;H;esm;H.js;xxHxx dynamicLazySequence.js;dynamicLazySequence;esm;dynamicLazySequence.js;xxdynamicLazySequencexx dynamicLazySequence2.js;dynamicLazySequence2;esm;dynamicLazySequence2.js;xxdynamicLazySequence2xx dynamicSubFile.js;dynamicSubFile;esm;dynamicSubFile.js;xxdynamicSubFilexx -moduleLazyImport.js;moduleLazyImport;esm;moduleLazyImport.js;xxmoduleLazyImportxx \ No newline at end of file +moduleLazyImport.js;moduleLazyImport;esm;moduleLazyImport.js;xxmoduleLazyImportxx +asyncUseLazyImport.ts;asyncUseLazyImport;esm;asyncUseLazyImport.ts;xxasyncUseLazyImportxx +lazyExport.js;lazyExport;esm;lazyExport.js;xxlazyExportxx \ No newline at end of file