diff --git a/unity/native_src/Inc/BackendEnv.h b/unity/native_src/Inc/BackendEnv.h index 40724dfb3f..51040006b1 100644 --- a/unity/native_src/Inc/BackendEnv.h +++ b/unity/native_src/Inc/BackendEnv.h @@ -140,7 +140,7 @@ namespace puerts v8::MaybeLocal _ResolveModule( v8::Local Context, v8::Local Specifier, - v8::Local Referrer, + v8::Local Referrer, bool& isFromCache ); @@ -148,6 +148,8 @@ namespace puerts bool LinkModule(v8::Local Context, v8::Local RefModule); + v8::MaybeLocal DynamicImport(v8::Local Context, v8::Local Referrer, v8::Local Specifier); + void HostInitializeImportMetaObject(v8::Local Context, v8::Local Module, v8::Local meta); #else JSModuleDef* js_module_loader(JSContext* ctx, const char *name, void *opaque); diff --git a/unity/native_src/Src/BackendEnv.cpp b/unity/native_src/Src/BackendEnv.cpp index 5f067fef94..ce34fd9b5f 100644 --- a/unity/native_src/Src/BackendEnv.cpp +++ b/unity/native_src/Src/BackendEnv.cpp @@ -321,7 +321,7 @@ void puerts::BackendEnv::InitInject(v8::Isolate* Isolate, v8::Local NodeIsolateData = node::CreateIsolateData(Isolate, &NodeUVLoop, Platform, NodeArrayBufferAllocator.get()); // node::FreeIsolateData - NodeEnv = CreateEnvironment(NodeIsolateData, Context, *Args, *ExecArgs, node::EnvironmentFlags::kOwnsProcessState); + NodeEnv = CreateEnvironment(NodeIsolateData, Context, *Args, *ExecArgs, (node::EnvironmentFlags::Flags)(node::EnvironmentFlags::kOwnsProcessState | node::EnvironmentFlags::kNoRegisterESMLoader | node::EnvironmentFlags::kNoCreateInspector)); Global->Set(Context, strConsole, Console).Check(); @@ -341,6 +341,7 @@ void puerts::BackendEnv::InitInject(v8::Isolate* Isolate, v8::Local #if !WITH_QUICKJS Isolate->SetHostInitializeImportMetaObjectCallback(&puerts::esmodule::HostInitializeImportMetaObject); + Isolate->SetHostImportModuleDynamicallyCallback(&puerts::esmodule::DynamicImport); #endif Context->Global()->Set(Context, v8::String::NewFromUtf8(Isolate, "__tgjsSetPromiseRejectCallback").ToLocalChecked(), v8::FunctionTemplate::New(Isolate, &SetPromiseRejectCallback)->GetFunction(Context).ToLocalChecked()).Check(); @@ -479,6 +480,51 @@ static v8::MaybeLocal CallRead( return maybeRet; } +#if !WITH_QUICKJS +v8::MaybeLocal puerts::esmodule::DynamicImport( + v8::Local Context, + v8::Local Referrer, + v8::Local Specifier +) +{ + bool isFromCache; + v8::Local ReferrerName = Referrer->GetResourceName(); + + v8::TryCatch TryCatch(Context->GetIsolate()); + v8::MaybeLocal mod = puerts::esmodule::_ResolveModule(Context, Specifier, ReferrerName, isFromCache); + + v8::Local resolver; + if (!v8::Promise::Resolver::New(Context).ToLocal(&resolver)) return v8::MaybeLocal {}; + + if (mod.IsEmpty()) + { + resolver->Reject(Context, TryCatch.Exception()); + return resolver->GetPromise(); + } + v8::Local moduleChecked = mod.ToLocalChecked(); + if (!puerts::esmodule::LinkModule(Context, moduleChecked)) + { + resolver->Reject(Context, TryCatch.Exception()); + return resolver->GetPromise(); + } + v8::Maybe ret = moduleChecked->InstantiateModule(Context, puerts::esmodule::ResolveModule); + if (ret.IsNothing() || !ret.ToChecked()) + { + resolver->Reject(Context, TryCatch.Exception()); + return resolver->GetPromise(); + } + v8::MaybeLocal evalRet = moduleChecked->Evaluate(Context); + if (evalRet.IsEmpty()) + { + resolver->Reject(Context, TryCatch.Exception()); + return resolver->GetPromise(); + } + + resolver->Resolve(Context, moduleChecked->GetModuleNamespace()); + + return resolver->GetPromise(); +} +#endif void puerts::esmodule::ExecuteModule(const v8::FunctionCallbackInfo& info) { @@ -587,28 +633,12 @@ void puerts::esmodule::ExecuteModule(const v8::FunctionCallbackInfo& v8::MaybeLocal puerts::esmodule::_ResolveModule( v8::Local Context, v8::Local Specifier, - v8::Local Referrer, + v8::Local ReferrerName, bool& isFromCache ) { v8::Isolate* Isolate = Context->GetIsolate(); - BackendEnv* mm = (BackendEnv*)Isolate->GetData(1); - - v8::Local ReferrerName; -#if V8_94_OR_NEWER - const auto referIter = mm->ScriptIdToPathMap.find(Referrer->ScriptId()); -#else - const auto referIter = mm->ScriptIdToPathMap.find(Referrer->GetIdentityHash()); -#endif - if (referIter != mm->ScriptIdToPathMap.end()) - { - std::string referPath_std = referIter->second; - ReferrerName = v8::String::NewFromUtf8(Isolate, referPath_std.c_str()).ToLocalChecked(); - } - else - { - ReferrerName = v8::String::NewFromUtf8(Isolate, "").ToLocalChecked(); - } + BackendEnv* mm = BackendEnv::Get(Isolate); v8::MaybeLocal maybeRet = CallResolver(Isolate, Context, Specifier, ReferrerName); if (maybeRet.IsEmpty()) @@ -668,14 +698,41 @@ v8::MaybeLocal puerts::esmodule::_ResolveModule( return Module; } +v8::Local GetModuleName( + v8::Isolate* Isolate, + v8::Local Referrer +) +{ + puerts::BackendEnv* mm = puerts::BackendEnv::Get(Isolate); + v8::Local ReferrerName; +#if V8_94_OR_NEWER + const auto referIter = mm->ScriptIdToPathMap.find(Referrer->ScriptId()); +#else + const auto referIter = mm->ScriptIdToPathMap.find(Referrer->GetIdentityHash()); +#endif + if (referIter != mm->ScriptIdToPathMap.end()) + { + std::string referPath_std = referIter->second; + ReferrerName = v8::String::NewFromUtf8(Isolate, referPath_std.c_str()).ToLocalChecked(); + } + else + { + ReferrerName = v8::String::NewFromUtf8(Isolate, "").ToLocalChecked(); + } + return ReferrerName; +} + v8::MaybeLocal puerts::esmodule::ResolveModule( v8::Local Context, v8::Local Specifier, v8::Local Referrer ) { + v8::Isolate* Isolate = Context->GetIsolate(); bool isFromCache = false; - return _ResolveModule(Context, Specifier, Referrer, isFromCache); + v8::Local ReferrerName = GetModuleName(Isolate, Referrer); + + return _ResolveModule(Context, Specifier, ReferrerName, isFromCache); } bool puerts::esmodule::LinkModule( @@ -690,7 +747,7 @@ bool puerts::esmodule::LinkModule( v8::Local Specifier_v8 = RefModule->GetModuleRequest(i); bool isFromCache = false; - v8::MaybeLocal MaybeModule = _ResolveModule(Context, Specifier_v8, RefModule, isFromCache); + v8::MaybeLocal MaybeModule = _ResolveModule(Context, Specifier_v8, GetModuleName(Isolate, RefModule), isFromCache); if (MaybeModule.IsEmpty()) { return false; @@ -710,7 +767,7 @@ bool puerts::esmodule::LinkModule( void puerts::esmodule::HostInitializeImportMetaObject(v8::Local Context, v8::Local Module, v8::Local meta) { v8::Isolate* Isolate = Context->GetIsolate(); - BackendEnv* mm = (BackendEnv*)Isolate->GetData(1); + BackendEnv* mm = BackendEnv::Get(Isolate); #if V8_94_OR_NEWER auto iter = mm->ScriptIdToPathMap.find(Module->ScriptId()); @@ -734,7 +791,7 @@ char* puerts::esmodule::js_module_resolver( { JSRuntime *rt = JS_GetRuntime(ctx); v8::Isolate* Isolate = (v8::Isolate*)JS_GetRuntimeOpaque(rt); - BackendEnv* mm = (BackendEnv*)Isolate->GetData(1); + BackendEnv* mm = BackendEnv::Get(Isolate); v8::Local Context = Isolate->GetCurrentContext(); v8::Local Specifier = v8::String::NewFromUtf8(Isolate, name).ToLocalChecked(); @@ -771,7 +828,7 @@ JSModuleDef* puerts::esmodule::js_module_loader( { JSRuntime *rt = JS_GetRuntime(ctx); v8::Isolate* Isolate = (v8::Isolate*)JS_GetRuntimeOpaque(rt); - BackendEnv* mm = (BackendEnv*)Isolate->GetData(1); + BackendEnv* mm = BackendEnv::Get(Isolate); v8::Local Context = Isolate->GetCurrentContext(); std::string name_std(name, strlen(name)); diff --git a/unity/test/Src/Cases/API/EvalTest.cs b/unity/test/Src/Cases/API/EvalTest.cs index 75bb4923c7..27fed34732 100644 --- a/unity/test/Src/Cases/API/EvalTest.cs +++ b/unity/test/Src/Cases/API/EvalTest.cs @@ -311,5 +311,63 @@ public void ESModuleExecuteCJSRelative() jsEnv.Tick(); } + + + [Test] + public void ESDynamicModuleNotFound() + { + var jsEnv = UnitTestEnv.GetEnv(); + jsEnv.Eval(@" + import('notfound/whatever.mjs') + .catch(e => { + global.error_ESDynamicModuleNotFound = e + }) + "); + jsEnv.Tick(); + StringAssert.Contains("notfound/whatever.mjs", jsEnv.Eval("error_ESDynamicModuleNotFound.toString()")); + } +#if !UNITY_WEBGL || UNITY_EDITOR + [Test] + public void ESDynamicModuleCompileError() + { + var loader = UnitTestEnv.GetLoader(); + loader.AddMockFileContent("compile-error/whatever2.mjs", @"export delete;"); + var jsEnv = UnitTestEnv.GetEnv(); + jsEnv.Eval(@" + import('compile-error/whatever2.mjs') + .catch(e => { + global.error_ESDynamicModuleCompileError = e + }) + "); + jsEnv.Tick(); + StringAssert.Contains("export", jsEnv.Eval("error_ESDynamicModuleCompileError.toString()")); + } +#endif + [Test] + public void ESDynamicModuleEvaluateError() + { + var jsEnv = UnitTestEnv.GetEnv(); + jsEnv.Eval(@" + import('eval-error/whatever.mjs') + .catch(e => { + global.error_ESDynamicModuleEvaluateError = e + }) + "); + jsEnv.Tick(); + StringAssert.Contains("not a function", jsEnv.Eval("error_ESDynamicModuleEvaluateError.toString()")); + } + [Test] + public void ESDynamicModuleImportRelative() + { + var jsEnv = UnitTestEnv.GetEnv(); + jsEnv.Eval(@" + import('import-relative/a/entry.mjs') + .then(r => { + global.result_ESDynamicModuleImportRelative = r.str + }) + "); + jsEnv.Tick(); + StringAssert.Contains("hello", jsEnv.Eval("result_ESDynamicModuleImportRelative.toString()")); + } } } \ No newline at end of file