[browser] loader ES6 module (#85730)
authorPavel Savara <pavel.savara@gmail.com>
Thu, 11 May 2023 19:02:02 +0000 (21:02 +0200)
committerGitHub <noreply@github.com>
Thu, 11 May 2023 19:02:02 +0000 (21:02 +0200)
118 files changed:
eng/liveBuilds.targets
eng/testing/tests.browser.targets
src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/SecondRuntimeTest.js
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
src/mono/sample/wasm/Directory.Build.targets
src/mono/sample/wasm/browser-advanced/index.html
src/mono/sample/wasm/browser-bench/appstart-frame.html
src/mono/sample/wasm/browser-webpack/Wasm.Browser.WebPack.Sample.csproj
src/mono/sample/wasm/node-webpack/Wasm.Node.WebPack.Sample.csproj
src/mono/wasm/README.md
src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs
src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests2.cs
src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs
src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/SimpleSourceChangeRebuildTest.cs
src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs
src/mono/wasm/build/WasmApp.Native.targets
src/mono/wasm/build/WasmApp.targets
src/mono/wasm/runtime/CMakeLists.txt
src/mono/wasm/runtime/assets.ts
src/mono/wasm/runtime/cancelable-promise.ts
src/mono/wasm/runtime/class-loader.ts
src/mono/wasm/runtime/config.ts [deleted file]
src/mono/wasm/runtime/cwraps.ts
src/mono/wasm/runtime/diagnostics-mock.d.ts
src/mono/wasm/runtime/diagnostics-mock.d.ts.sha256
src/mono/wasm/runtime/diagnostics/index.ts
src/mono/wasm/runtime/diagnostics/mock/environment.ts
src/mono/wasm/runtime/diagnostics/mock/export-types.ts
src/mono/wasm/runtime/diagnostics/mock/index.ts
src/mono/wasm/runtime/diagnostics/mock/types.ts
src/mono/wasm/runtime/diagnostics/server_pthread/index.ts
src/mono/wasm/runtime/diagnostics/server_pthread/ipc-protocol/serializer.ts
src/mono/wasm/runtime/diagnostics/server_pthread/mock-remote.ts
src/mono/wasm/runtime/diagnostics/server_pthread/protocol-socket.ts
src/mono/wasm/runtime/diagnostics/server_pthread/socket-connection.ts
src/mono/wasm/runtime/dotnet-legacy.d.ts
src/mono/wasm/runtime/dotnet.d.ts
src/mono/wasm/runtime/es6/dotnet.es6.extpost.js
src/mono/wasm/runtime/es6/dotnet.es6.lib.js
src/mono/wasm/runtime/es6/dotnet.es6.pre.js
src/mono/wasm/runtime/export-api.ts
src/mono/wasm/runtime/exports.ts
src/mono/wasm/runtime/gc-handles.ts
src/mono/wasm/runtime/globals.ts
src/mono/wasm/runtime/http.ts
src/mono/wasm/runtime/hybrid-globalization.ts
src/mono/wasm/runtime/icu.ts
src/mono/wasm/runtime/invoke-cs.ts
src/mono/wasm/runtime/invoke-js.ts
src/mono/wasm/runtime/jiterpreter-interp-entry.ts
src/mono/wasm/runtime/jiterpreter-jit-call.ts
src/mono/wasm/runtime/jiterpreter-support.ts
src/mono/wasm/runtime/jiterpreter-trace-generator.ts
src/mono/wasm/runtime/jiterpreter.ts
src/mono/wasm/runtime/loader/assets.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/blazor/BootConfig.ts [moved from src/mono/wasm/runtime/blazor/BootConfig.ts with 50% similarity]
src/mono/wasm/runtime/loader/blazor/WebAssemblyResourceLoader.ts [moved from src/mono/wasm/runtime/blazor/WebAssemblyResourceLoader.ts with 98% similarity]
src/mono/wasm/runtime/loader/blazor/_Integration.ts [moved from src/mono/wasm/runtime/blazor/_Integration.ts with 73% similarity]
src/mono/wasm/runtime/loader/blazor/_Polyfill.ts [moved from src/mono/wasm/runtime/blazor/_Polyfill.ts with 87% similarity]
src/mono/wasm/runtime/loader/config.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/exit.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/globals.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/icu.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/index.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/logging.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/polyfills.ts [new file with mode: 0644]
src/mono/wasm/runtime/loader/promise-controller.ts [moved from src/mono/wasm/runtime/promise-controller.ts with 69% similarity]
src/mono/wasm/runtime/loader/run.ts [moved from src/mono/wasm/runtime/run-outer.ts with 75% similarity]
src/mono/wasm/runtime/loader/worker.ts [new file with mode: 0644]
src/mono/wasm/runtime/logging.ts
src/mono/wasm/runtime/managed-exports.ts
src/mono/wasm/runtime/marshal-to-cs.ts
src/mono/wasm/runtime/marshal-to-js.ts
src/mono/wasm/runtime/marshal.ts
src/mono/wasm/runtime/memory.ts
src/mono/wasm/runtime/modularize-dotnet.md
src/mono/wasm/runtime/net6-legacy/buffers.ts
src/mono/wasm/runtime/net6-legacy/corebindings.ts
src/mono/wasm/runtime/net6-legacy/cs-to-js.ts
src/mono/wasm/runtime/net6-legacy/export-types.ts
src/mono/wasm/runtime/net6-legacy/exports-legacy.ts
src/mono/wasm/runtime/net6-legacy/globals.ts
src/mono/wasm/runtime/net6-legacy/js-to-cs.ts
src/mono/wasm/runtime/net6-legacy/method-binding.ts
src/mono/wasm/runtime/net6-legacy/method-calls.ts
src/mono/wasm/runtime/polyfills.ts
src/mono/wasm/runtime/profiler.ts
src/mono/wasm/runtime/promise-utils.ts [deleted file]
src/mono/wasm/runtime/pthreads/browser/index.ts
src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts
src/mono/wasm/runtime/pthreads/shared/index.ts
src/mono/wasm/runtime/pthreads/worker/index.ts
src/mono/wasm/runtime/rollup.config.js
src/mono/wasm/runtime/roots.ts
src/mono/wasm/runtime/run.ts
src/mono/wasm/runtime/snapshot.ts
src/mono/wasm/runtime/startup.ts
src/mono/wasm/runtime/strings.ts
src/mono/wasm/runtime/types/blazor.ts [new file with mode: 0644]
src/mono/wasm/runtime/types/consts.d.ts
src/mono/wasm/runtime/types/export-types.ts [moved from src/mono/wasm/runtime/export-types.ts with 80% similarity]
src/mono/wasm/runtime/types/index.ts [moved from src/mono/wasm/runtime/types-api.ts with 94% similarity]
src/mono/wasm/runtime/types/internal.ts [moved from src/mono/wasm/runtime/types.ts with 74% similarity]
src/mono/wasm/runtime/web-socket.ts
src/mono/wasm/runtime/workers/README.md
src/mono/wasm/symbolicator/Program.cs
src/mono/wasm/test-main.js
src/mono/wasm/wasm.proj
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs
src/tasks/WasmAppBuilder/WasmAppBuilder.cs
src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs

index 5f7dcb8..f0a62d0 100644 (file)
       <LibrariesRuntimeFiles Condition="'$(TargetOS)' == 'browser'"
                              Include="
         $(LibrariesNativeArtifactsPath)dotnet.js;
+        $(LibrariesNativeArtifactsPath)dotnet.native.js;
+        $(LibrariesNativeArtifactsPath)dotnet.runtime.js;
         $(LibrariesNativeArtifactsPath)dotnet.d.ts;
         $(LibrariesNativeArtifactsPath)dotnet-legacy.d.ts;
         $(LibrariesNativeArtifactsPath)package.json;
-        $(LibrariesNativeArtifactsPath)dotnet.wasm;
-        $(LibrariesNativeArtifactsPath)dotnet.js.symbols;
+        $(LibrariesNativeArtifactsPath)dotnet.native.wasm;
+        $(LibrariesNativeArtifactsPath)dotnet.native.js.symbols;
         $(LibrariesNativeArtifactsPath)*.dat;"
         IsNative="true" />
       <!-- for threaded wasm -->
index d358982..ab394db 100644 (file)
     <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'"     >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode)</_XHarnessArgs>
     <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''"         >$(_XHarnessArgs) $(WasmXHarnessArgs)</_XHarnessArgs>
     <_XHarnessArgs Condition="('$(WasmEnableThreads)' == 'true' or '$(WasmEnablePerfTracing)' == 'true') and '$(_XHarnessArs.Contains(&quot;--web-server-use-cop&quot;)' != 'true'">$(_XHarnessArgs) --web-server-use-cop</_XHarnessArgs>
-    <_XHarnessArgs                                                 >$(_XHarnessArgs) -s dotnet.js.symbols</_XHarnessArgs>
+    <_XHarnessArgs                                                 >$(_XHarnessArgs) -s dotnet.native.js.symbols</_XHarnessArgs>
     <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbol-patterns wasm-symbol-patterns.txt</_XHarnessArgs>
     <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbolicator WasmSymbolicator.dll,Microsoft.WebAssembly.Internal.SymbolicatorWrapperForXHarness</_XHarnessArgs>
     <_XHarnessArgs Condition="'$(_WasmBrowserPathForTests)' != ''" >$(_XHarnessArgs) &quot;--browser-path=$(_WasmBrowserPathForTests)&quot;</_XHarnessArgs>
index 5760758..14c9aa9 100644 (file)
     <PlatformManifestFileEntry Include="libmono-wasm-eh-wasm.a" IsNative="true" />
     <PlatformManifestFileEntry Include="wasm-bundled-timezones.a" IsNative="true" />
     <PlatformManifestFileEntry Include="dotnet.js" IsNative="true" />
-    <PlatformManifestFileEntry Include="dotnet.worker.js" IsNative="true" />
-    <PlatformManifestFileEntry Include="dotnet.js.symbols" IsNative="true" />
+    <PlatformManifestFileEntry Include="dotnet.runtime.js" IsNative="true" />
+    <PlatformManifestFileEntry Include="dotnet.native.js" IsNative="true" />
+    <PlatformManifestFileEntry Include="dotnet.native.worker.js" IsNative="true" />
+    <PlatformManifestFileEntry Include="dotnet.native.js.symbols" IsNative="true" />
     <PlatformManifestFileEntry Include="dotnet.d.ts" IsNative="true" />
     <PlatformManifestFileEntry Include="dotnet-legacy.d.ts" IsNative="true" />
-    <PlatformManifestFileEntry Include="dotnet.wasm" IsNative="true" />
+    <PlatformManifestFileEntry Include="dotnet.native.wasm" IsNative="true" />
     <PlatformManifestFileEntry Include="icudt.dat" IsNative="true" />
     <PlatformManifestFileEntry Include="icudt_no_CJK.dat" IsNative="true" />
     <PlatformManifestFileEntry Include="icudt_CJK.dat" IsNative="true" />
     <PlatformManifestFileEntry Include="icudt_optimal_no_CJK.dat" IsNative="true" />
     <PlatformManifestFileEntry Include="package.json" IsNative="true" />
     <PlatformManifestFileEntry Include="pal_random.lib.js" IsNative="true" />
-    <PlatformManifestFileEntry Include="runtime.es6.iffe.js" IsNative="true" />
     <PlatformManifestFileEntry Include="dotnet.es6.pre.js" IsNative="true" />
     <PlatformManifestFileEntry Include="dotnet.es6.lib.js" IsNative="true" />
     <PlatformManifestFileEntry Include="dotnet.es6.extpost.js" IsNative="true" />
     <PlatformManifestFileEntry Include="driver.h" IsNative="true" />
     <PlatformManifestFileEntry Include="stubs.c" IsNative="true" />
     <PlatformManifestFileEntry Include="synthetic-pthread.c" IsNative="true" />
+    <PlatformManifestFileEntry Include="dotnet.wasm" IsNative="true" />
     <!-- ICU-specific files -->
     <PlatformManifestFileEntry Include="libicudata.a" IsNative="true" />
     <PlatformManifestFileEntry Include="libicui18n.a" IsNative="true" />
index 7abbf83..fa0b5d9 100644 (file)
@@ -1,6 +1,11 @@
 export async function runSecondRuntimeAndTestStaticState() {
-    const { dotnet: dotnet2 } = await import('./dotnet.js?2');
-    const runtime2 = await dotnet2.create();
+    const { dotnet: dotnet2 } = await import('./dotnet.js?instance=2');
+    const runtime2 = await dotnet2
+        .withStartupMemoryCache(false)
+        .withConfig({
+            assetUniqueQuery: "?instance=2",
+        })
+        .create();
 
     const increment1 = await getIncrementStateFunction(App.runtime);
     const increment2 = await getIncrementStateFunction(runtime2);
index 47b4cf3..78f63db 100644 (file)
@@ -189,7 +189,7 @@ Copyright (c) .NET Foundation. All rights reserved.
 
       <!-- Remove dotnet.js/wasm from runtime pack, in favor of the relinked ones in @(WasmNativeAsset) -->
       <ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)"
-                               Condition="@(WasmNativeAsset->Count()) > 0 and '%(FileName)' == 'dotnet' and ('%(Extension)' == '.wasm' or '%(Extension)' == '.js')" />
+                               Condition="@(WasmNativeAsset->Count()) > 0 and ( '%(FileName)' == 'dotnet' or '%(FileName)' == 'dotnet.native' ) and ('%(Extension)' == '.wasm' or '%(Extension)' == '.js')" />
     </ItemGroup>
 
     <ComputeWasmBuildAssets
index 7ff3c4d..88aceec 100644 (file)
@@ -31,7 +31,8 @@
       $(TargetFileName)
       "
       Outputs="
-      bin/$(Configuration)/AppBundle/dotnet.wasm;
+      bin/$(Configuration)/AppBundle/dotnet.native.wasm;
+      bin/$(Configuration)/AppBundle/dotnet.native.js;
       bin/$(Configuration)/AppBundle/$(_WasmMainJSFileName);
       ">
     <PropertyGroup>
index 0532ef8..3489daf 100644 (file)
@@ -10,7 +10,9 @@
   <script type='module' src="./main.js"></script>
   <script type='module' src="./dotnet.js"></script>
   <link rel="preload" href="./mono-config.json" as="fetch" crossorigin="anonymous">
-  <link rel="prefetch" href="./dotnet.wasm" as="fetch" crossorigin="anonymous">
+  <link rel="prefetch" href="./dotnet.native.js" as="fetch" crossorigin="anonymous">
+  <link rel="prefetch" href="./dotnet.runtime.js" as="fetch" crossorigin="anonymous">
+  <link rel="prefetch" href="./dotnet.native.wasm" as="fetch" crossorigin="anonymous">
   <!-- users should consider if they optimize for the first load or subsequent load from memory snapshot -->
   <link rel="prefetch" href="./icudt.dat" as="fetch" crossorigin="anonymous">
   <link rel="prefetch" href="./managed/System.Private.CoreLib.webcil" as="fetch" crossorigin="anonymous">
index 4a1945c..fbb1c16 100644 (file)
@@ -10,7 +10,9 @@
   <script type="module" src="./frame-main.js"></script>
   <script type='module' src="./dotnet.js"></script>
   <link rel="preload" href="./mono-config.json" as="fetch" crossorigin="anonymous">
-  <link rel="prefetch" href="./dotnet.wasm" as="fetch" crossorigin="anonymous">
+  <link rel="prefetch" href="./dotnet.native.js" as="fetch" crossorigin="anonymous">
+  <link rel="prefetch" href="./dotnet.runtime.js" as="fetch" crossorigin="anonymous">
+  <link rel="prefetch" href="./dotnet.native.wasm" as="fetch" crossorigin="anonymous">
   <!-- users should consider if they optimize for the first load or subsequent load from memory snapshot -->
   <link rel="prefetch" href="./managed/System.Private.CoreLib.dll" as="fetch" crossorigin="anonymous">
 </head>
index cc956fb..bb694f2 100644 (file)
 
 
   <Target Name="CopyRelinkedPackage" AfterTargets="WasmBuildApp" DependsOnTargets="Build" Inputs="$(WasmAppDir)/dotnet.js;
-          $(WasmAppDir)/dotnet.wasm;
+          $(WasmAppDir)/dotnet.runtime.js;
+          $(WasmAppDir)/dotnet.native.js;
+          $(WasmAppDir)/dotnet.native.wasm;
           $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts;
           $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet-legacy.d.ts;
           $(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json;" Outputs="bin/dotnet-runtime/.npm-stamp">
     <ItemGroup>
       <NpmPackageFiles Include="$(WasmAppDir)/dotnet.js"/>
-      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.wasm"/>
+      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.runtime.js"/>
+      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.native.js"/>
+      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.native.wasm"/>
       <NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts"/>
       <NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet-legacy.d.ts"/>
       <NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json"/>
index c8e0219..b10e782 100644 (file)
@@ -5,13 +5,17 @@
   </PropertyGroup>
 
   <Target Name="CopyRelinkedPackage" AfterTargets="WasmBuildApp" DependsOnTargets="Build" Inputs="$(WasmAppDir)/dotnet.js;
-          $(WasmAppDir)/dotnet.wasm;
+          $(WasmAppDir)/dotnet.runtime.js;
+          $(WasmAppDir)/dotnet.native.js;
+          $(WasmAppDir)/dotnet.native.wasm;
           $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts;
           $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet-legacy.d.ts;
           $(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json;" Outputs="bin/dotnet-runtime/.npm-stamp">
     <ItemGroup>
       <NpmPackageFiles Include="$(WasmAppDir)/dotnet.js"/>
-      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.wasm"/>
+      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.runtime.js"/>
+      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.native.js"/>
+      <NpmPackageFiles Include="$(WasmAppDir)/dotnet.native.wasm"/>
       <NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts"/>
       <NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet-legacy.d.ts"/>
       <NpmPackageFiles Include="$(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json"/>
index cfeea4b..e266fe3 100644 (file)
@@ -133,13 +133,13 @@ The wrapper script used to actually run these tests, accepts:
 
 Exceptions thrown after the runtime starts get symbolicating from js itself. Exceptions before that, like asserts containing native traces get symbolicated by xharness using `src/mono/wasm/symbolicator`.
 
-If you need to symbolicate some traces manually, then you need the corresponding `dotnet.js.symbols` file. Then:
+If you need to symbolicate some traces manually, then you need the corresponding `dotnet.native.js.symbols` file. Then:
 
 ```console
-src/mono/wasm/symbolicator$ dotnet run /path/to/dotnet.js.symbols /path/to/file/with/traces
+src/mono/wasm/symbolicator$ dotnet run /path/to/dotnet.native.js.symbols /path/to/file/with/traces
 ```
 
-When not relinking, or not building with AOT, you can find `dotnet.js.symbols` in the runtime pack.
+When not relinking, or not building with AOT, you can find `dotnet.native.js.symbols` in the runtime pack.
 
 ## Debugger tests on macOS
 
index 3e2c0b2..363635e 100644 (file)
@@ -33,11 +33,11 @@ public class BuildPublishTests : BuildTestBase
 
         // Build
         BlazorBuildInternal(id, config, publish: false);
-        AssertBlazorBootJson(config, isPublish: false);
+        AssertBlazorBootJson(config, isPublish: false, isNet7AndBelow: false);
 
         // Publish
         BlazorBuildInternal(id, config, publish: true);
-        AssertBlazorBootJson(config, isPublish: true);
+        AssertBlazorBootJson(config, isPublish: true, isNet7AndBelow: false);
     }
 
     [Theory]
index 1c8dd2a..467a235 100644 (file)
@@ -127,7 +127,7 @@ public class MiscTests2 : BuildTestBase
             Assert.Contains("** UsingBrowserRuntimeWorkload: 'false'", result.Output);
 
             string binFrameworkDir = FindBlazorBinFrameworkDir(config, forPublish: true, framework: "net5.0");
-            AssertBlazorBootJson(config, isPublish: true, binFrameworkDir: binFrameworkDir);
+            AssertBlazorBootJson(config, isPublish: true, isNet7AndBelow: true, binFrameworkDir: binFrameworkDir);
             // dotnet.wasm here would be from 5.0 nuget like:
             // /Users/radical/.nuget/packages/microsoft.netcore.app.runtime.browser-wasm/5.0.9/runtimes/browser-wasm/native/dotnet.wasm
         }
index 6dc3e5e..cea2456 100644 (file)
@@ -621,22 +621,22 @@ namespace Wasm.Build.Tests
                 _ => throw new ArgumentOutOfRangeException(nameof(type))
             };
 
-            AssertSameFile(Path.Combine(srcDir, "dotnet.wasm"), Path.Combine(binFrameworkDir, "dotnet.wasm"), label);
+            AssertSameFile(Path.Combine(srcDir, "dotnet.native.wasm"), Path.Combine(binFrameworkDir, "dotnet.native.wasm"), label);
 
             // find dotnet*js
             string? dotnetJsPath = Directory.EnumerateFiles(binFrameworkDir)
-                                    .Where(p => Path.GetFileName(p).StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
+                                    .Where(p => Path.GetFileName(p).StartsWith("dotnet.native", StringComparison.OrdinalIgnoreCase) &&
                                                     Path.GetFileName(p).EndsWith(".js", StringComparison.OrdinalIgnoreCase))
                                     .SingleOrDefault();
 
-            Assert.True(!string.IsNullOrEmpty(dotnetJsPath), $"[{label}] Expected to find dotnet*js in {binFrameworkDir}");
-            AssertSameFile(Path.Combine(srcDir, "dotnet.js"), dotnetJsPath!, label);
+            Assert.True(!string.IsNullOrEmpty(dotnetJsPath), $"[{label}] Expected to find dotnet.native*js in {binFrameworkDir}");
+            AssertSameFile(Path.Combine(srcDir, "dotnet.native.js"), dotnetJsPath!, label);
 
             if (type != NativeFilesType.FromRuntimePack)
             {
                 // check that the files are *not* from runtime pack
-                AssertNotSameFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.wasm"), Path.Combine(binFrameworkDir, "dotnet.wasm"), label);
-                AssertNotSameFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.js"), dotnetJsPath!, label);
+                AssertNotSameFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.wasm"), Path.Combine(binFrameworkDir, "dotnet.native.wasm"), label);
+                AssertNotSameFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.js"), dotnetJsPath!, label);
             }
         }
 
@@ -667,9 +667,11 @@ namespace Wasm.Build.Tests
             var filesToExist = new List<string>()
             {
                 mainJS,
-                "dotnet.wasm",
+                "dotnet.native.wasm",
                 "mono-config.json",
-                "dotnet.js"
+                "dotnet.js",
+                "dotnet.native.js",
+                "dotnet.runtime.js"
             };
 
             if (isBrowserProject)
@@ -751,20 +753,20 @@ namespace Wasm.Build.Tests
 
         protected static void AssertDotNetWasmJs(string bundleDir, bool fromRuntimePack, string targetFramework)
         {
-            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.wasm"),
-                       Path.Combine(bundleDir, "dotnet.wasm"),
-                       "Expected dotnet.wasm to be same as the runtime pack",
+            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.wasm"),
+                       Path.Combine(bundleDir, "dotnet.native.wasm"),
+                       "Expected dotnet.native.wasm to be same as the runtime pack",
                        same: fromRuntimePack);
 
-            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.js"),
-                       Path.Combine(bundleDir, "dotnet.js"),
-                       "Expected dotnet.js to be same as the runtime pack",
+            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.js"),
+                       Path.Combine(bundleDir, "dotnet.native.js"),
+                       "Expected dotnet.native.js to be same as the runtime pack",
                        same: fromRuntimePack);
         }
 
         protected static void AssertDotNetJsSymbols(string bundleDir, bool fromRuntimePack, string targetFramework)
-            => AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.js.symbols"),
-                            Path.Combine(bundleDir, "dotnet.js.symbols"),
+            => AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.js.symbols"),
+                            Path.Combine(bundleDir, "dotnet.native.js.symbols"),
                             same: fromRuntimePack);
 
         protected static void AssertFilesDontExist(string dir, string[] filenames, string? label = null)
@@ -819,22 +821,22 @@ namespace Wasm.Build.Tests
         {
             binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish, targetFramework);
 
-            AssertBlazorBootJson(config, isPublish, targetFramework, binFrameworkDir: binFrameworkDir);
-            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.wasm"),
-                       Path.Combine(binFrameworkDir, "dotnet.wasm"),
-                       "Expected dotnet.wasm to be same as the runtime pack",
+            AssertBlazorBootJson(config, isPublish, targetFramework != DefaultTargetFrameworkForBlazor, targetFramework, binFrameworkDir: binFrameworkDir);
+            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.wasm"),
+                       Path.Combine(binFrameworkDir, "dotnet.native.wasm"),
+                       "Expected dotnet.native.wasm to be same as the runtime pack",
                        same: dotnetWasmFromRuntimePack);
 
-            string? dotnetJsPath = Directory.EnumerateFiles(binFrameworkDir, "dotnet.*.js").FirstOrDefault();
+            string? dotnetJsPath = Directory.EnumerateFiles(binFrameworkDir, "dotnet.native.*.js").FirstOrDefault();
             Assert.True(dotnetJsPath != null, $"Could not find blazor's dotnet*js in {binFrameworkDir}");
 
-            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.js"),
+            AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.js"),
                         dotnetJsPath!,
-                        "Expected dotnet.js to be same as the runtime pack",
+                        "Expected dotnet.native.js to be same as the runtime pack",
                         same: dotnetWasmFromRuntimePack);
         }
 
-        protected void AssertBlazorBootJson(string config, bool isPublish, string targetFramework = DefaultTargetFrameworkForBlazor, string? binFrameworkDir=null)
+        protected void AssertBlazorBootJson(string config, bool isPublish, bool isNet7AndBelow, string targetFramework = DefaultTargetFrameworkForBlazor, string? binFrameworkDir=null)
         {
             binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish, targetFramework);
 
@@ -847,7 +849,7 @@ namespace Wasm.Build.Tests
             Assert.NotNull(runtimeObj);
 
             string msgPrefix=$"[{( isPublish ? "publish" : "build" )}]";
-            Assert.True(runtimeObj!.Where(kvp => kvp.Key == "dotnet.wasm").Any(), $"{msgPrefix} Could not find dotnet.wasm entry in blazor.boot.json");
+            Assert.True(runtimeObj!.Where(kvp => kvp.Key == (isNet7AndBelow ? "dotnet.wasm" : "dotnet.native.wasm")).Any(), $"{msgPrefix} Could not find dotnet.native.wasm entry in blazor.boot.json");
             Assert.True(runtimeObj!.Where(kvp => kvp.Key.StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
                                                     kvp.Key.EndsWith(".js", StringComparison.OrdinalIgnoreCase)).Any(),
                                             $"{msgPrefix} Could not find dotnet.*js in {bootJson}");
index ffbc0fd..67e7e32 100644 (file)
@@ -36,7 +36,7 @@ namespace Wasm.Build.NativeRebuild.Tests
             (buildArgs, BuildPaths paths) = FirstNativeBuild(s_mainReturns42, nativeRelink: true, invariant: false, buildArgs, id);
             var pathsDict = GetFilesTable(buildArgs, paths, unchanged: true);
             if (extraLDFlags.Length > 0)
-                pathsDict.UpdateTo(unchanged: false, "dotnet.wasm", "dotnet.js");
+                pathsDict.UpdateTo(unchanged: false, "dotnet.native.wasm", "dotnet.native.js");
 
             var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
 
index aac389b..d31f990 100644 (file)
@@ -164,8 +164,8 @@ namespace Wasm.Build.NativeRebuild.Tests
                 Path.Combine(paths.ObjWasmDir, "pinvoke-table.h"),
                 Path.Combine(paths.ObjWasmDir, "driver-gen.c"),
 
-                Path.Combine(paths.BundleDir, "dotnet.wasm"),
-                Path.Combine(paths.BundleDir, "dotnet.js")
+                Path.Combine(paths.BundleDir, "dotnet.native.wasm"),
+                Path.Combine(paths.BundleDir, "dotnet.native.js"),
             };
 
             if (buildArgs.AOT)
@@ -184,6 +184,10 @@ namespace Wasm.Build.NativeRebuild.Tests
             foreach (var file in files)
                 dict[Path.GetFileName(file)] = (file, unchanged);
 
+            // those files do not change on re-link
+            dict["dotnet.js"]=(Path.Combine(paths.BundleDir, "dotnet.js"), true);
+            dict["dotnet.runtime.js"]=(Path.Combine(paths.BundleDir, "dotnet.runtime.js"), true);
+
             return dict;
         }
     }
index e5f17d2..9f713fa 100644 (file)
@@ -79,7 +79,7 @@ namespace Wasm.Build.NativeRebuild.Tests
 
             var pathsDict = GetFilesTable(true, objDir);
             pathsDict.Remove("runtime-icall-table.h");
-            pathsDict.UpdateTo(unchanged: false, "dotnet.wasm", "dotnet.js", "emcc-link.rsp");
+            pathsDict.UpdateTo(unchanged: false, "dotnet.native.wasm", "dotnet.native.js", "emcc-link.rsp");
 
             var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
 
index bbe7d60..f2bfb33 100644 (file)
@@ -28,7 +28,7 @@ namespace Wasm.Build.NativeRebuild.Tests
             string mainAssembly = $"{buildArgs.ProjectName}.dll";
             var pathsDict = GetFilesTable(buildArgs, paths, unchanged: true);
             pathsDict.UpdateTo(unchanged: false, mainAssembly);
-            pathsDict.UpdateTo(unchanged: !buildArgs.AOT, "dotnet.wasm", "dotnet.js");
+            pathsDict.UpdateTo(unchanged: !buildArgs.AOT, "dotnet.native.wasm", "dotnet.native.js");
 
             if (buildArgs.AOT)
                 pathsDict.UpdateTo(unchanged: false, $"{mainAssembly}.bc", $"{mainAssembly}.o");
index 2d9100b..ea57866 100644 (file)
@@ -401,7 +401,7 @@ namespace Wasm.Build.Tests
             }
             else
             {
-                AssertFilesDontExist(Path.Combine(GetBinDir(config), "AppBundle"), new[] { "dotnet.js.symbols" });
+                AssertFilesDontExist(Path.Combine(GetBinDir(config), "AppBundle"), new[] { "dotnet.native.js.symbols" });
             }
 
             string runArgs = $"run --no-build -c {config}";
index d7afe27..85b5e18 100644 (file)
       <_EmccLinkStepArgs Include="&quot;%(_WasmNativeFileForLinking.Identity)&quot;" />
       <_WasmLinkDependencies Include="@(_WasmNativeFileForLinking)" />
 
-      <_EmccLinkStepArgs Include="-o &quot;$(_WasmIntermediateOutputPath)dotnet.js&quot;" />
+      <_EmccLinkStepArgs Include="-o &quot;$(_WasmIntermediateOutputPath)dotnet.native.js&quot;" />
       <_WasmLinkDependencies Include="$(_EmccLinkRsp)" />
 
       <_EmccLinkStepArgs Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" Condition="'$(_EmccExportedLibraryFunction)' != ''" />
 
   <Target Name="_WasmLinkDotNet"
           Inputs="@(_WasmLinkDependencies);$(_EmccDefaultFlagsRsp);$(_EmccDefaultLinkFlagsRsp);$(_EmccLinkRsp)"
-          Outputs="$(_WasmIntermediateOutputPath)dotnet.js;$(_WasmIntermediateOutputPath)dotnet.wasm"
+          Outputs="$(_WasmIntermediateOutputPath)dotnet.native.js;$(_WasmIntermediateOutputPath)dotnet.native.wasm"
           DependsOnTargets="_CheckEmccIsExpectedVersion;_WasmSelectRuntimeComponentsForLinking;_WasmCompileAssemblyBitCodeFilesForAOT;_WasmWriteRspFilesForLinking"
           Returns="@(FileWrites)" >
 
       <Output TaskParameter="ExitCode" PropertyName="_EmccLinkStepExitCode" />
     </Exec>
     <ItemGroup>
-      <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.wasm" />
-      <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.js" />
-      <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.js.symbols" Condition="'$(WasmEmitSymbolMap)' == 'true'" />
+      <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.native.wasm" />
+      <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.native.js" />
+      <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.native.js.symbols" Condition="'$(WasmEmitSymbolMap)' == 'true'" />
     </ItemGroup>
 
     <ItemGroup>
       <WasmOptConfigurationFlags Condition="'$(WasmOptConfigurationFlags)' != ''" Include="$(WasmOptConfigurationFlags)" />
     </ItemGroup>
 
-    <Message Text="Stripping symbols from dotnet.wasm ..." Importance="High" Condition="'$(WasmNativeStrip)' == 'true'" />
-    <Exec Command="wasm-opt$(_ExeExt) --enable-simd --enable-exception-handling @(WasmOptConfigurationFlags, ' ') --strip-dwarf &quot;$(_WasmIntermediateOutputPath)dotnet.wasm&quot; -o &quot;$(_WasmIntermediateOutputPath)dotnet.wasm&quot;"
+    <Message Text="Stripping symbols from dotnet.native.wasm ..." Importance="High" Condition="'$(WasmNativeStrip)' == 'true'" />
+    <Exec Command="wasm-opt$(_ExeExt) --enable-simd --enable-exception-handling @(WasmOptConfigurationFlags, ' ') --strip-dwarf &quot;$(_WasmIntermediateOutputPath)dotnet.native.wasm&quot; -o &quot;$(_WasmIntermediateOutputPath)dotnet.native.wasm&quot;"
           Condition="'$(WasmNativeStrip)' == 'true'"
           IgnoreStandardErrorWarningFormat="true"
           EnvironmentVariables="@(EmscriptenEnvVars)" />
 
   <Target Name="_CompleteWasmBuildNative">
     <ItemGroup>
-      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.wasm" />
-      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.js" />
-      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.worker.js" Condition="Exists('$(_WasmIntermediateOutputPath)dotnet.worker.js')" />
-      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.js.symbols" Condition="'$(WasmEmitSymbolMap)' == 'true' and Exists('$(_WasmIntermediateOutputPath)dotnet.js.symbols')" />
+      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.wasm" />
+      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.js" />
+      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.worker.js" Condition="Exists('$(_WasmIntermediateOutputPath)dotnet.worker.js')" />
+      <WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.js.symbols" Condition="'$(WasmEmitSymbolMap)' == 'true' and Exists('$(_WasmIntermediateOutputPath)dotnet.native.js.symbols')" />
       <_WasmAssembliesInternal Remove="$(_WasmDedupAssembly)"/>
     </ItemGroup>
   </Target>
index 0d50ada..94bd9fe 100644 (file)
@@ -22,7 +22,7 @@
 
       - $(WasmNativeDebugSymbols) - Build with native debug symbols, useful only with `$(RunAOTCompilation)`, or `$(WasmBuildNative)`
                                     Defaults to true.
-      - $(WasmEmitSymbolMap)      - Generates a `dotnet.js.symbols` file with a map of wasm function number to name.
+      - $(WasmEmitSymbolMap)      - Generates a `dotnet.native.js.symbols` file with a map of wasm function number to name.
       - $(WasmDedup)         - Whenever to dedup generic instances when using AOT. Defaults to true.
 
       - $(WasmProfilers)     - Profilers to use
     <Warning Condition="'$(InvariantGlobalization)' == 'true' and '$(HybridGlobalization)' == 'true'" Text="%24(HybridGlobalization) has no effect when %24(InvariantGlobalization) is set to true." />
     <PropertyGroup>
       <HybridGlobalization Condition="'$(InvariantGlobalization)' == 'true'">false</HybridGlobalization>
-      <_HasDotnetWasm Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.wasm'">true</_HasDotnetWasm>
-      <_HasDotnetJsWorker Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.worker.js'">true</_HasDotnetJsWorker>
-      <_HasDotnetJsSymbols Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js.symbols'">true</_HasDotnetJsSymbols>
-      <_HasDotnetJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">true</_HasDotnetJs>
+      <_HasDotnetWasm Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.native.wasm'">true</_HasDotnetWasm>
+      <_HasDotnetJsWorker Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.native.worker.js'">true</_HasDotnetJsWorker>
+      <_HasDotnetJsSymbols Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.native.js.symbols'">true</_HasDotnetJsSymbols>
+      <_HasDotnetNativeJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.native.js'">true</_HasDotnetNativeJs>
       <_WasmIcuDataFileName Condition="'$(WasmIcuDataFileName)' != '' and Exists('$(WasmIcuDataFileName)')">$(WasmIcuDataFileName)</_WasmIcuDataFileName>
       <_WasmIcuDataFileName Condition="'$(WasmIcuDataFileName)' != '' and !Exists('$(WasmIcuDataFileName)')">$(MicrosoftNetCoreAppRuntimePackRidNativeDir)$(WasmIcuDataFileName)</_WasmIcuDataFileName>
     </PropertyGroup>
 
     <ItemGroup>
       <!-- If dotnet.{wasm,js} weren't added already (eg. AOT can add them), then add the default ones -->
-      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.wasm" Condition="'$(_HasDotnetWasm)' != 'true'" />
-      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js" Condition="'$(_HasDotnetJs)' != 'true'" />
-      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.worker.js" Condition="'$(_HasDotnetJsWorker)' != 'true' and Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.worker.js')" />
-      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js.symbols"
+      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js" />
+      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.runtime.js" />
+      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.wasm" Condition="'$(_HasDotnetWasm)' != 'true'" />
+      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.js" Condition="'$(_HasDotnetNativeJs)' != 'true'" />
+      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.worker.js" Condition="'$(_HasDotnetJsWorker)' != 'true' and Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.worker.js')" />
+      <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.js.symbols"
                        Condition="'$(WasmEmitSymbolMap)' == 'true' and
                                   '$(_HasDotnetJsSymbols)' != 'true' and
-                                  Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js.symbols')" />
+                                  Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.js.symbols')" />
     </ItemGroup>
 
     <ItemGroup Condition="'$(InvariantGlobalization)' != 'true'">
index 1a39d15..bb311e3 100644 (file)
@@ -7,14 +7,14 @@ option(DISABLE_WASM_USER_THREADS "defined if the build does not allow user threa
 option(DISABLE_LEGACY_JS_INTEROP "defined if the build does not support legacy JavaScript interop" OFF)
 
 set(CMAKE_EXECUTABLE_SUFFIX ".js")
-add_executable(dotnet corebindings.c driver.c pinvoke.c)
+add_executable(dotnet.native corebindings.c driver.c pinvoke.c)
 
-target_include_directories(dotnet PUBLIC ${MONO_INCLUDES} ${MONO_OBJ_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}/include/wasm)
-target_compile_options(dotnet PUBLIC @${NATIVE_BIN_DIR}/src/emcc-default.rsp @${NATIVE_BIN_DIR}/src/emcc-compile.rsp -DGEN_PINVOKE=1)
+target_include_directories(dotnet.native PUBLIC ${MONO_INCLUDES} ${MONO_OBJ_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}/include/wasm)
+target_compile_options(dotnet.native PUBLIC @${NATIVE_BIN_DIR}/src/emcc-default.rsp @${NATIVE_BIN_DIR}/src/emcc-compile.rsp -DGEN_PINVOKE=1)
 
-set_target_properties(dotnet PROPERTIES COMPILE_FLAGS ${CONFIGURATION_EMCC_FLAGS})
+set_target_properties(dotnet.native PROPERTIES COMPILE_FLAGS ${CONFIGURATION_EMCC_FLAGS})
 
-target_link_libraries(dotnet
+target_link_libraries(dotnet.native
     PRIVATE
     ${ICU_LIB_DIR}/libicuuc.a
     ${ICU_LIB_DIR}/libicui18n.a
@@ -33,9 +33,9 @@ target_link_libraries(dotnet
     ${NATIVE_BIN_DIR}/libSystem.Globalization.Native.a
     ${NATIVE_BIN_DIR}/libSystem.IO.Compression.Native.a)
 
-set_target_properties(dotnet PROPERTIES
-    LINK_DEPENDS "${NATIVE_BIN_DIR}/src/emcc-default.rsp;${NATIVE_BIN_DIR}/src/es6/dotnet.es6.pre.js;${NATIVE_BIN_DIR}/src/es6/runtime.es6.iffe.js;${NATIVE_BIN_DIR}/src/es6/dotnet.es6.lib.js;${NATIVE_BIN_DIR}/src/pal_random.lib.js;${NATIVE_BIN_DIR}/src/es6/dotnet.es6.extpost.js;"
-    LINK_FLAGS "@${NATIVE_BIN_DIR}/src/emcc-default.rsp @${NATIVE_BIN_DIR}/src/emcc-link.rsp ${CONFIGURATION_LINK_FLAGS} --extern-pre-js ${NATIVE_BIN_DIR}/src/es6/runtime.es6.iffe.js --pre-js ${NATIVE_BIN_DIR}/src/es6/dotnet.es6.pre.js --js-library ${NATIVE_BIN_DIR}/src/es6/dotnet.es6.lib.js --js-library ${NATIVE_BIN_DIR}/src/pal_random.lib.js --extern-post-js ${NATIVE_BIN_DIR}/src/es6/dotnet.es6.extpost.js "
+set_target_properties(dotnet.native PROPERTIES
+    LINK_DEPENDS "${NATIVE_BIN_DIR}/src/emcc-default.rsp;${NATIVE_BIN_DIR}/src/es6/dotnet.es6.pre.js;${NATIVE_BIN_DIR}/src/es6/dotnet.es6.lib.js;${NATIVE_BIN_DIR}/src/pal_random.lib.js;${NATIVE_BIN_DIR}/src/es6/dotnet.es6.extpost.js;"
+    LINK_FLAGS "@${NATIVE_BIN_DIR}/src/emcc-default.rsp @${NATIVE_BIN_DIR}/src/emcc-link.rsp ${CONFIGURATION_LINK_FLAGS} --pre-js ${NATIVE_BIN_DIR}/src/es6/dotnet.es6.pre.js --js-library ${NATIVE_BIN_DIR}/src/es6/dotnet.es6.lib.js --js-library ${NATIVE_BIN_DIR}/src/pal_random.lib.js --extern-post-js ${NATIVE_BIN_DIR}/src/es6/dotnet.es6.extpost.js "
     RUNTIME_OUTPUT_DIRECTORY "${NATIVE_BIN_DIR}")
 
 set(ignoreMeWasmOptFlags "${CONFIGURATION_WASM_OPT_FLAGS}")
@@ -43,9 +43,9 @@ set(ignoreMeWasmOptAdditionalFlags "${WASM_OPT_ADDITIONAL_FLAGS}")
 set(ignoreMeEmsdkPath "${EMSDK_PATH}")
 
 if(CMAKE_BUILD_TYPE STREQUAL "Release")
-    add_custom_command(TARGET dotnet
-                        POST_BUILD COMMAND ${EMSDK_PATH}/upstream/bin/wasm-opt --enable-exception-handling ${CONFIGURATION_WASM_OPT_FLAGS} --strip-dwarf ${NATIVE_BIN_DIR}/dotnet.wasm -o ${NATIVE_BIN_DIR}/dotnet.wasm
-                        COMMENT "Stripping debug symbols from dotnet.wasm using wasm-opt")
+    add_custom_command(TARGET dotnet.native
+                        POST_BUILD COMMAND ${EMSDK_PATH}/upstream/bin/wasm-opt --enable-exception-handling ${CONFIGURATION_WASM_OPT_FLAGS} --strip-dwarf ${NATIVE_BIN_DIR}/dotnet.native.wasm -o ${NATIVE_BIN_DIR}/dotnet.native.wasm
+                        COMMENT "Stripping debug symbols from dotnet.native.wasm using wasm-opt")
 endif()
 
 configure_file(wasm-config.h.in include/wasm/wasm-config.h)
index 3797753..489f482 100644 (file)
 
 import cwraps from "./cwraps";
 import { mono_wasm_load_icu_data } from "./icu";
-import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, Module, runtimeHelpers } from "./globals";
+import { ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, Module, loaderHelpers, runtimeHelpers } from "./globals";
 import { parseSymbolMapFile } from "./logging";
 import { mono_wasm_load_bytes_into_heap } from "./memory";
 import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
-import { createPromiseController, PromiseAndController } from "./promise-controller";
-import { delay } from "./promise-utils";
-import { abort_startup, beforeOnRuntimeInitialized, memorySnapshotSkippedOrDone } from "./startup";
-import { AssetEntryInternal, mono_assert } from "./types";
-import { AssetBehaviours, AssetEntry, LoadingResource, ResourceRequest } from "./types-api";
+import { AssetEntryInternal } from "./types/internal";
+import { AssetEntry } from "./types";
 import { InstantiateWasmSuccessCallback, VoidPtr } from "./types/emscripten";
 
-const allAssetsInMemory = createPromiseController<void>();
-const allDownloadsQueued = createPromiseController<void>();
-let actual_downloaded_assets_count = 0;
-let actual_instantiated_assets_count = 0;
-let expected_downloaded_assets_count = 0;
-let expected_instantiated_assets_count = 0;
-const loaded_files: { url: string, file: string }[] = [];
-// in order to prevent net::ERR_INSUFFICIENT_RESOURCES if we start downloading too many files at same time
-let parallel_count = 0;
-let throttlingPromise: PromiseAndController<void> | undefined;
-
-// don't `fetch` javaScript files
-const skipDownloadsByAssetTypes: {
-    [k: string]: boolean
-} = {
-    "js-module-threads": true,
-    "dotnetwasm": true,
-};
-
-// `response.arrayBuffer()` can't be called twice. Some usecases are calling it on response in the instantiation.
-const skipBufferByAssetTypes: {
-    [k: string]: boolean
-} = {
-    "dotnetwasm": true,
-    "symbols": true,
-};
-
-const containedInSnapshotByAssetTypes: {
-    [k: string]: boolean
-} = {
-    "resource": true,
-    "assembly": true,
-    "pdb": true,
-    "heap": true,
-    "icu": true,
-    "js-module-threads": true,
-    "dotnetwasm": true,
-};
-
-
-// these assets are instantiated differently than the main flow
-const skipInstantiateByAssetTypes: {
-    [k: string]: boolean
-} = {
-    "js-module-threads": true,
-    "dotnetwasm": true,
-    "symbols": true,
-};
-
-export function shouldLoadIcuAsset(asset: AssetEntryInternal): boolean {
-    return !(asset.behavior == "icu" && asset.name != runtimeHelpers.preferredIcuAsset);
-}
-
-export function resolve_asset_path(behavior: AssetBehaviours) {
-    const asset: AssetEntryInternal | undefined = runtimeHelpers.config.assets?.find(a => a.behavior == behavior);
-    mono_assert(asset, () => `Can't find asset for ${behavior}`);
-    if (!asset.resolvedUrl) {
-        asset.resolvedUrl = resolve_path(asset, "");
-    }
-    return asset;
-}
-export async function mono_download_assets(): Promise<void> {
-    if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_download_assets");
-    runtimeHelpers.maxParallelDownloads = runtimeHelpers.config.maxParallelDownloads || runtimeHelpers.maxParallelDownloads;
-    runtimeHelpers.enableDownloadRetry = runtimeHelpers.config.enableDownloadRetry || runtimeHelpers.enableDownloadRetry;
-    try {
-        const alwaysLoadedAssets: AssetEntryInternal[] = [];
-        const containedInSnapshotAssets: AssetEntryInternal[] = [];
-        const promises_of_assets: Promise<AssetEntryInternal>[] = [];
-
-        for (const a of runtimeHelpers.config.assets!) {
-            const asset: AssetEntryInternal = a;
-            mono_assert(typeof asset === "object", "asset must be object");
-            mono_assert(typeof asset.behavior === "string", "asset behavior must be known string");
-            mono_assert(typeof asset.name === "string", "asset name must be string");
-            mono_assert(!asset.resolvedUrl || typeof asset.resolvedUrl === "string", "asset resolvedUrl could be string");
-            mono_assert(!asset.hash || typeof asset.hash === "string", "asset resolvedUrl could be string");
-            mono_assert(!asset.pendingDownload || typeof asset.pendingDownload === "object", "asset pendingDownload could be object");
-            if (containedInSnapshotByAssetTypes[asset.behavior]) {
-                containedInSnapshotAssets.push(asset);
-            } else {
-                alwaysLoadedAssets.push(asset);
-            }
-        }
-
-        const countAndStartDownload = (asset: AssetEntryInternal) => {
-            if (!skipInstantiateByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
-                expected_instantiated_assets_count++;
-            }
-            if (!skipDownloadsByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
-                expected_downloaded_assets_count++;
-                promises_of_assets.push(start_asset_download(asset));
-            }
-        };
-
-        // start fetching assets in parallel, only assets which are not part of memory snapshot
-        for (const asset of alwaysLoadedAssets) {
-            countAndStartDownload(asset);
-        }
-
-        // continue after we know if memory snapshot is available or not
-        await memorySnapshotSkippedOrDone.promise;
-
-        // start fetching assets in parallel, only if memory snapshot is not available.
-        for (const asset of containedInSnapshotAssets) {
-            if (!runtimeHelpers.loadedMemorySnapshot) {
-                countAndStartDownload(asset);
-            } else {
-                // Otherwise cleanup in case we were given pending download. It would be even better if we could abort the download.
-                cleanupAsset(asset);
-                // tell the debugger it is loaded
-                if (asset.behavior == "resource" || asset.behavior == "assembly" || asset.behavior == "pdb") {
-                    const url = resolve_path(asset, "");
-                    const virtualName: string = typeof (asset.virtualPath) === "string"
-                        ? asset.virtualPath
-                        : asset.name;
-                    loaded_files.push({ url: url, file: virtualName });
-                }
-            }
-        }
-
-        allDownloadsQueued.promise_control.resolve();
-
-        const promises_of_asset_instantiation: Promise<void>[] = [];
-        for (const downloadPromise of promises_of_assets) {
-            promises_of_asset_instantiation.push((async () => {
-                const asset = await downloadPromise;
-                if (asset.buffer) {
-                    if (!skipInstantiateByAssetTypes[asset.behavior]) {
-                        const url = asset.pendingDownloadInternal!.url;
-                        mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array or buffer like");
-                        const data = new Uint8Array(asset.buffer!);
-                        cleanupAsset(asset);
-
-                        // wait till after onRuntimeInitialized and after memory snapshot is loaded or skipped
-                        await memorySnapshotSkippedOrDone.promise;
-                        await beforeOnRuntimeInitialized.promise;
-                        _instantiate_asset(asset, url, data);
-                    }
-                    if (asset.behavior === "symbols") {
-                        await instantiate_symbols_asset(asset);
-                        cleanupAsset(asset);
-                    }
-                } else {
-                    const headersOnly = skipBufferByAssetTypes[asset.behavior];
-                    if (!headersOnly) {
-                        mono_assert(asset.isOptional, "Expected asset to have the downloaded buffer");
-                        if (!skipDownloadsByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
-                            expected_downloaded_assets_count--;
-                        }
-                        if (!skipInstantiateByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
-                            expected_instantiated_assets_count--;
-                        }
-                    } else {
-                        if (skipBufferByAssetTypes[asset.behavior]) {
-                            ++actual_downloaded_assets_count;
-                        }
-                    }
-                }
-            })());
-        }
-
-        // this await will get past the onRuntimeInitialized because we are not blocking via addRunDependency
-        // and we are not awating it here
-        Promise.all(promises_of_asset_instantiation).then(() => {
-            allAssetsInMemory.promise_control.resolve();
-        }).catch(e => {
-            Module.err("MONO_WASM: Error in mono_download_assets: " + e);
-            abort_startup(e, true);
-        });
-        // OPTIMIZATION explained:
-        // we do it this way so that we could allocate memory immediately after asset is downloaded (and after onRuntimeInitialized which happened already)
-        // spreading in time
-        // rather than to block all downloads after onRuntimeInitialized or block onRuntimeInitialized after all downloads are done. That would create allocation burst.
-    } catch (e: any) {
-        Module.err("MONO_WASM: Error in mono_download_assets: " + e);
-        throw e;
-    }
-}
-
-// FIXME: Connection reset is probably the only good one for which we should retry
-export async function start_asset_download(asset: AssetEntryInternal): Promise<AssetEntryInternal> {
-    try {
-        return await start_asset_download_with_throttle(asset);
-    } catch (err: any) {
-        if (!runtimeHelpers.enableDownloadRetry) {
-            // we will not re-try if disabled
-            throw err;
-        }
-        if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) {
-            // we will not re-try on shell
-            throw err;
-        }
-        if (asset.pendingDownload && asset.pendingDownloadInternal == asset.pendingDownload) {
-            // we will not re-try with external source
-            throw err;
-        }
-        if (asset.resolvedUrl && asset.resolvedUrl.indexOf("file://") != -1) {
-            // we will not re-try with local file
-            throw err;
-        }
-        if (err && err.status == 404) {
-            // we will not re-try with 404
-            throw err;
-        }
-        asset.pendingDownloadInternal = undefined;
-        // second attempt only after all first attempts are queued
-        await allDownloadsQueued.promise;
-        try {
-            return await start_asset_download_with_throttle(asset);
-        } catch (err) {
-            asset.pendingDownloadInternal = undefined;
-            // third attempt after small delay
-            await delay(100);
-            return await start_asset_download_with_throttle(asset);
-        }
-    }
-}
-
-async function start_asset_download_with_throttle(asset: AssetEntryInternal): Promise<AssetEntryInternal> {
-    // we don't addRunDependency to allow download in parallel with onRuntimeInitialized event!
-    while (throttlingPromise) {
-        await throttlingPromise.promise;
-    }
-    try {
-        ++parallel_count;
-        if (parallel_count == runtimeHelpers.maxParallelDownloads) {
-            if (runtimeHelpers.diagnosticTracing)
-                console.debug("MONO_WASM: Throttling further parallel downloads");
-            throttlingPromise = createPromiseController<void>();
-        }
-
-        const response = await start_asset_download_sources(asset);
-        if (!response) {
-            return asset;
-        }
-        const skipBuffer = skipBufferByAssetTypes[asset.behavior];
-        if (skipBuffer) {
-            return asset;
-        }
-        asset.buffer = await response.arrayBuffer();
-        ++actual_downloaded_assets_count;
-        return asset;
-    }
-    finally {
-        --parallel_count;
-        if (throttlingPromise && parallel_count == runtimeHelpers.maxParallelDownloads - 1) {
-            if (runtimeHelpers.diagnosticTracing)
-                console.debug("MONO_WASM: Resuming more parallel downloads");
-            const old_throttling = throttlingPromise;
-            throttlingPromise = undefined;
-            old_throttling.promise_control.resolve();
-        }
-    }
-}
-
-async function start_asset_download_sources(asset: AssetEntryInternal): Promise<Response | undefined> {
-    // we don't addRunDependency to allow download in parallel with onRuntimeInitialized event!
-    if (asset.pendingDownload) {
-        asset.pendingDownloadInternal = asset.pendingDownload;
-    }
-    if (asset.pendingDownloadInternal && asset.pendingDownloadInternal.response) {
-        return asset.pendingDownloadInternal.response;
-    }
-    if (asset.buffer) {
-        const buffer = asset.buffer;
-        asset.buffer = null as any; // GC
-        asset.pendingDownloadInternal = {
-            url: "undefined://" + asset.name,
-            name: asset.name,
-            response: Promise.resolve({
-                arrayBuffer: () => buffer,
-                headers: {
-                    get: () => undefined,
-                }
-            }) as any
-        };
-        return asset.pendingDownloadInternal.response;
-    }
-
-    const sourcesList = asset.loadRemote && runtimeHelpers.config.remoteSources ? runtimeHelpers.config.remoteSources : [""];
-    let response: Response | undefined = undefined;
-    for (let sourcePrefix of sourcesList) {
-        sourcePrefix = sourcePrefix.trim();
-        // HACK: Special-case because MSBuild doesn't allow "" as an attribute
-        if (sourcePrefix === "./")
-            sourcePrefix = "";
-
-        const attemptUrl = resolve_path(asset, sourcePrefix);
-        if (asset.name === attemptUrl) {
-            if (runtimeHelpers.diagnosticTracing)
-                console.debug(`MONO_WASM: Attempting to download '${attemptUrl}'`);
-        } else {
-            if (runtimeHelpers.diagnosticTracing)
-                console.debug(`MONO_WASM: Attempting to download '${attemptUrl}' for ${asset.name}`);
-        }
-        try {
-            asset.resolvedUrl = attemptUrl;
-            const loadingResource = download_resource(asset);
-            asset.pendingDownloadInternal = loadingResource;
-            response = await loadingResource.response;
-            if (!response || !response.ok) {
-                continue;// next source
-            }
-            return response;
-        }
-        catch (err) {
-            if (!response) {
-                response = {
-                    ok: false,
-                    url: attemptUrl,
-                    status: 0,
-                    statusText: "" + err,
-                } as any;
-            }
-            continue; //next source
-        }
-    }
-    const isOkToFail = asset.isOptional || (asset.name.match(/\.pdb$/) && runtimeHelpers.config.ignorePdbLoadErrors);
-    mono_assert(response, () => `Response undefined ${asset.name}`);
-    if (!isOkToFail) {
-        const err: any = new Error(`MONO_WASM: download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`);
-        err.status = response.status;
-        throw err;
-    } else {
-        Module.out(`MONO_WASM: optional download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`);
-        return undefined;
-    }
-}
-
-function resolve_path(asset: AssetEntry, sourcePrefix: string): string {
-    mono_assert(sourcePrefix !== null && sourcePrefix !== undefined, () => `sourcePrefix must be provided for ${asset.name}`);
-    let attemptUrl;
-    const assemblyRootFolder = runtimeHelpers.config.assemblyRootFolder;
-    if (!asset.resolvedUrl) {
-        if (sourcePrefix === "") {
-            if (asset.behavior === "assembly" || asset.behavior === "pdb") {
-                attemptUrl = assemblyRootFolder
-                    ? (assemblyRootFolder + "/" + asset.name)
-                    : asset.name;
-            }
-            else if (asset.behavior === "resource") {
-                const path = asset.culture && asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name;
-                attemptUrl = assemblyRootFolder
-                    ? (assemblyRootFolder + "/" + path)
-                    : path;
-            }
-            else {
-                attemptUrl = asset.name;
-            }
-        } else {
-            attemptUrl = sourcePrefix + asset.name;
-        }
-        attemptUrl = runtimeHelpers.locateFile(attemptUrl);
-    }
-    else {
-        attemptUrl = asset.resolvedUrl;
-    }
-    mono_assert(attemptUrl && typeof attemptUrl == "string", "attemptUrl need to be path or url string");
-    return attemptUrl;
-}
-
-function download_resource(request: ResourceRequest): LoadingResource {
-    try {
-        if (typeof Module.downloadResource === "function") {
-            const loading = Module.downloadResource(request);
-            if (loading) return loading;
-        }
-        const options: any = {};
-        if (request.hash) {
-            options.integrity = request.hash;
-        }
-        const response = runtimeHelpers.fetch_like(request.resolvedUrl!, options);
-        return {
-            name: request.name, url: request.resolvedUrl!, response
-        };
-    } catch (err) {
-        const response = <Response><any>{
-            ok: false,
-            url: request.resolvedUrl,
-            status: 500,
-            statusText: "ERR29: " + err,
-            arrayBuffer: () => { throw err; },
-            json: () => { throw err; }
-        };
-        return {
-            name: request.name, url: request.resolvedUrl!, response: Promise.resolve(response)
-        };
-    }
-}
-
 // this need to be run only after onRuntimeInitialized event, when the memory is ready
-function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
+export function instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array): void {
     if (runtimeHelpers.diagnosticTracing)
         console.debug(`MONO_WASM: Loaded:${asset.name} as ${asset.behavior} size ${bytes.length} from ${url}`);
     const mark = startMeasure();
@@ -425,7 +31,7 @@ function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
         case "resource":
         case "assembly":
         case "pdb":
-            loaded_files.push({ url: url, file: virtualName });
+            loaderHelpers._loaded_files.push({ url: url, file: virtualName });
         // falls through
         case "heap":
         case "icu":
@@ -475,8 +81,8 @@ function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
         const hasPpdb = cwraps.mono_wasm_add_assembly(virtualName, offset!, bytes.length);
 
         if (!hasPpdb) {
-            const index = loaded_files.findIndex(element => element.file == virtualName);
-            loaded_files.splice(index, 1);
+            const index = loaderHelpers._loaded_files.findIndex(element => element.file == virtualName);
+            loaderHelpers._loaded_files.splice(index, 1);
         }
     }
     else if (asset.behavior === "pdb") {
@@ -490,7 +96,7 @@ function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
         cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture || "", offset!, bytes.length);
     }
     endMeasure(mark, MeasuredBlock.instantiateAsset, asset.name);
-    ++actual_instantiated_assets_count;
+    ++loaderHelpers.actual_instantiated_assets_count;
 }
 
 export async function instantiate_wasm_asset(
@@ -498,7 +104,7 @@ export async function instantiate_wasm_asset(
     wasmModuleImports: WebAssembly.Imports,
     successCallback: InstantiateWasmSuccessCallback,
 ): Promise<void> {
-    mono_assert(pendingAsset && pendingAsset.pendingDownloadInternal && pendingAsset.pendingDownloadInternal.response, "Can't load dotnet.wasm");
+    mono_assert(pendingAsset && pendingAsset.pendingDownloadInternal && pendingAsset.pendingDownloadInternal.response, "Can't load dotnet.native.wasm");
     const response = await pendingAsset.pendingDownloadInternal.response;
     const contentType = response.headers && response.headers.get ? response.headers.get("Content-Type") : undefined;
     let compiledInstance: WebAssembly.Instance;
@@ -527,9 +133,7 @@ export async function instantiate_wasm_asset(
     successCallback(compiledInstance, compiledModule);
 }
 
-export async function instantiate_symbols_asset(
-    pendingAsset: AssetEntryInternal,
-): Promise<void> {
+export async function instantiate_symbols_asset(pendingAsset: AssetEntryInternal): Promise<void> {
     try {
         const response = await pendingAsset.pendingDownloadInternal!.response;
         const text = await response.text();
@@ -590,23 +194,16 @@ export function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): b
 
 export async function wait_for_all_assets() {
     // wait for all assets in memory
-    await allAssetsInMemory.promise;
+    await runtimeHelpers.allAssetsInMemory.promise;
     if (runtimeHelpers.config.assets) {
-        mono_assert(actual_downloaded_assets_count == expected_downloaded_assets_count, () => `Expected ${expected_downloaded_assets_count} assets to be downloaded, but only finished ${actual_downloaded_assets_count}`);
-        mono_assert(actual_instantiated_assets_count == expected_instantiated_assets_count, () => `Expected ${expected_instantiated_assets_count} assets to be in memory, but only instantiated ${actual_instantiated_assets_count}`);
-        loaded_files.forEach(value => runtimeHelpers.loadedFiles.push(value.url));
+        mono_assert(loaderHelpers.actual_downloaded_assets_count == loaderHelpers.expected_downloaded_assets_count, () => `Expected ${loaderHelpers.expected_downloaded_assets_count} assets to be downloaded, but only finished ${loaderHelpers.actual_downloaded_assets_count}`);
+        mono_assert(loaderHelpers.actual_instantiated_assets_count == loaderHelpers.expected_instantiated_assets_count, () => `Expected ${loaderHelpers.expected_instantiated_assets_count} assets to be in memory, but only instantiated ${loaderHelpers.actual_instantiated_assets_count}`);
+        loaderHelpers._loaded_files.forEach(value => loaderHelpers.loadedFiles.push(value.url));
         if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: all assets are loaded in wasm memory");
     }
 }
 
 // Used by the debugger to enumerate loaded dlls and pdbs
 export function mono_wasm_get_loaded_files(): string[] {
-    return runtimeHelpers.loadedFiles;
-}
-
-export function cleanupAsset(asset: AssetEntryInternal) {
-    // give GC chance to collect resources
-    asset.pendingDownloadInternal = null as any; // GC
-    asset.pendingDownload = null as any; // GC
-    asset.buffer = null as any; // GC
+    return loaderHelpers.loadedFiles;
 }
\ No newline at end of file
index b50e0b0..1f696a1 100644 (file)
@@ -2,9 +2,9 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import { _lookup_js_owned_object } from "./gc-handles";
+import { createPromiseController, loaderHelpers } from "./globals";
 import { TaskCallbackHolder } from "./marshal-to-cs";
-import { mono_assert, GCHandle } from "./types";
-import { createPromiseController, getPromiseController, ControllablePromise, assertIsControllablePromise } from "./promise-controller";
+import { ControllablePromise, GCHandle } from "./types/internal";
 
 export const _are_promises_supported = ((typeof Promise === "object") || (typeof Promise === "function")) && (typeof Promise.resolve === "function");
 
@@ -29,8 +29,8 @@ export function mono_wasm_cancel_promise(task_holder_gc_handle: GCHandle): void
 
     const promise = holder.promise;
     mono_assert(!!promise, () => `Expected Promise for GCHandle ${task_holder_gc_handle}`);
-    assertIsControllablePromise(promise);
-    const promise_control = getPromiseController(promise);
+    loaderHelpers.assertIsControllablePromise(promise);
+    const promise_control = loaderHelpers.getPromiseController(promise);
     promise_control.reject("OperationCanceledException");
 }
 
index 378b619..892aa7b 100644 (file)
@@ -1,4 +1,4 @@
-import { MonoAssembly, MonoClass, MonoType, MonoTypeNull, MonoAssemblyNull } from "./types";
+import { MonoAssembly, MonoClass, MonoType, MonoTypeNull, MonoAssemblyNull } from "./types/internal";
 import cwraps from "./cwraps";
 
 const _assembly_cache_by_name = new Map<string, MonoAssembly>();
@@ -38,40 +38,40 @@ function _set_cached_class(assembly: MonoAssembly, namespace: string, name: stri
     classes.set(name, ptr);
 }
 
-export function find_corlib_class(namespace: string, name: string, throw_on_failure?: boolean | undefined): MonoClass {
+export function find_corlib_class(namespace: string, name: string): MonoClass {
     if (!_corlib)
         _corlib = cwraps.mono_wasm_get_corlib();
     let result = _find_cached_class(_corlib, namespace, name);
     if (result !== undefined)
         return result;
     result = cwraps.mono_wasm_assembly_find_class(_corlib, namespace, name);
-    if (throw_on_failure && !result)
+    if (!result)
         throw new Error(`Failed to find corlib class ${namespace}.${name}`);
-    _set_cached_class(_corlib, namespace, name, result);
+    _set_cached_class(_corlib, namespace, name, result!);
     return result;
 }
 
-export function find_class_in_assembly(assembly_name: string, namespace: string, name: string, throw_on_failure?: boolean | undefined): MonoClass {
+export function find_class_in_assembly(assembly_name: string, namespace: string, name: string): MonoClass {
     const assembly = assembly_load(assembly_name);
     let result = _find_cached_class(assembly, namespace, name);
     if (result !== undefined)
         return result;
     result = cwraps.mono_wasm_assembly_find_class(assembly, namespace, name);
-    if (throw_on_failure && !result)
+    if (!result)
         throw new Error(`Failed to find class ${namespace}.${name} in ${assembly_name}`);
-    _set_cached_class(assembly, namespace, name, result);
+    _set_cached_class(assembly, namespace, name, result!);
     return result;
 }
 
-export function find_corlib_type(namespace: string, name: string, throw_on_failure?: boolean | undefined): MonoType {
-    const classPtr = find_corlib_class(namespace, name, throw_on_failure);
+export function find_corlib_type(namespace: string, name: string): MonoType {
+    const classPtr = find_corlib_class(namespace, name);
     if (!classPtr)
         return MonoTypeNull;
     return cwraps.mono_wasm_class_get_type(classPtr);
 }
 
-export function find_type_in_assembly(assembly_name: string, namespace: string, name: string, throw_on_failure?: boolean | undefined): MonoType {
-    const classPtr = find_class_in_assembly(assembly_name, namespace, name, throw_on_failure);
+export function find_type_in_assembly(assembly_name: string, namespace: string, name: string): MonoType {
+    const classPtr = find_class_in_assembly(assembly_name, namespace, name);
     if (!classPtr)
         return MonoTypeNull;
     return cwraps.mono_wasm_class_get_type(classPtr);
diff --git a/src/mono/wasm/runtime/config.ts b/src/mono/wasm/runtime/config.ts
deleted file mode 100644 (file)
index 34e92f0..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { DotnetModuleInternal, MonoConfigInternal } from "./types";
-import { DotnetModuleConfig } from "./types-api";
-
-export function deep_merge_config(target: MonoConfigInternal, source: MonoConfigInternal): MonoConfigInternal {
-    const providedConfig: MonoConfigInternal = { ...source };
-    if (providedConfig.assets) {
-        providedConfig.assets = [...(target.assets || []), ...(providedConfig.assets || [])];
-    }
-    if (providedConfig.environmentVariables) {
-        providedConfig.environmentVariables = { ...(target.environmentVariables || {}), ...(providedConfig.environmentVariables || {}) };
-    }
-    if (providedConfig.startupOptions) {
-        providedConfig.startupOptions = { ...(target.startupOptions || {}), ...(providedConfig.startupOptions || {}) };
-    }
-    if (providedConfig.runtimeOptions) {
-        providedConfig.runtimeOptions = [...(target.runtimeOptions || []), ...(providedConfig.runtimeOptions || [])];
-    }
-    return Object.assign(target, providedConfig);
-}
-
-export function deep_merge_module(target: DotnetModuleInternal, source: DotnetModuleConfig): DotnetModuleInternal {
-    const providedConfig: DotnetModuleConfig = { ...source };
-    if (providedConfig.config) {
-        if (!target.config) target.config = {};
-        providedConfig.config = deep_merge_config(target.config, providedConfig.config);
-    }
-    return Object.assign(target, providedConfig);
-}
\ No newline at end of file
index 9540663..225cdef 100644 (file)
@@ -5,7 +5,7 @@ import type {
     MonoArray, MonoAssembly, MonoClass,
     MonoMethod, MonoObject, MonoString,
     MonoType, MonoObjectRef, MonoStringRef, JSMarshalerArguments
-} from "./types";
+} from "./types/internal";
 import type { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten";
 import WasmEnableLegacyJsInterop from "consts:WasmEnableLegacyJsInterop";
 import { disableLegacyJsInterop, Module } from "./globals";
index b715324..59686e8 100644 (file)
@@ -5,7 +5,6 @@
 
 //! This is not considered public API with backward compatibility guarantees. 
 
-declare const promise_control_symbol: unique symbol;
 interface PromiseController<T = any> {
     isDone: boolean;
     readonly promise: Promise<T>;
@@ -13,7 +12,7 @@ interface PromiseController<T = any> {
     reject: (reason?: any) => void;
 }
 interface ControllablePromise<T = any> extends Promise<T> {
-    [promise_control_symbol]: PromiseController<T>;
+    __brand: "ControllablePromise";
 }
 interface PromiseAndController<T> {
     promise: ControllablePromise<T>;
index 1b6e1a2..0cfcc26 100644 (file)
@@ -1 +1 @@
-90231971bda6e6a32b2c5239c60176b9126469d29478f572b8a15ab7476cd791
\ No newline at end of file
+6d0ff454946223f77abe8e6c1e377489c33b2914da86120f6b2952b739ebec20
\ No newline at end of file
index 4987795..2dea3bf 100644 (file)
@@ -5,7 +5,7 @@ import monoWasmThreads from "consts:monoWasmThreads";
 import type {
     DiagnosticOptions,
 } from "./shared/types";
-import { is_nullish } from "../types";
+import { is_nullish } from "../types/internal";
 import type { VoidPtr } from "../types/emscripten";
 import { getController, startDiagnosticServer } from "./browser/controller";
 import * as memory from "../memory";
index c0508ca..d3d4822 100644 (file)
@@ -1,14 +1,13 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { createPromiseController } from "../../promise-controller";
 import type { RemoveCommandSetAndId, EventPipeCommandCollectTracing2, EventPipeCommandStopTracing } from "../server_pthread/protocol-client-commands";
 import type { FilterPredicate, MockEnvironment } from "./types";
 import Serializer from "../server_pthread/ipc-protocol/base-serializer";
 import { CommandSetId, EventPipeCommandId, ProcessCommandId } from "../server_pthread/ipc-protocol/types";
-import { assertNever, mono_assert } from "../../types";
-import { delay } from "../../promise-utils";
+import { assertNever } from "../../types/internal";
 import { pthread_self } from "../../pthreads/worker";
+import { createPromiseController } from "../../globals";
 
 
 function expectAdvertise(data: ArrayBuffer): boolean {
@@ -133,7 +132,7 @@ export function createMockEnvironment(): MockEnvironment {
         postMessageToBrowser,
         addEventListenerFromBrowser,
         createPromiseController,
-        delay,
+        delay: (ms: number) => new Promise(resolve => setTimeout(resolve, ms)),
         command,
         reply,
         expectAdvertise
index 1117fcc..6628e0c 100644 (file)
@@ -8,4 +8,4 @@ export type {
 
 export type {
     PromiseAndController,
-} from "../../promise-controller";
+} from "../../types/internal";
index 38f4186..30d0801 100644 (file)
@@ -5,7 +5,7 @@ import monoDiagnosticsMock from "consts:monoDiagnosticsMock";
 
 import { createMockEnvironment } from "./environment";
 import type { MockEnvironment, MockScriptConnection } from "./export-types";
-import { assertNever } from "../../types";
+import { assertNever } from "../../types/internal";
 
 export interface MockRemoteSocket extends EventTarget {
     addEventListener<T extends keyof WebSocketEventMap>(type: T, listener: (this: MockRemoteSocket, ev: WebSocketEventMap[T]) => any, options?: boolean | AddEventListenerOptions): void;
index 92f8fe4..30c5335 100644 (file)
@@ -1,6 +1,6 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
-import type { PromiseAndController } from "../../promise-controller";
+import type { PromiseAndController } from "../../types/internal";
 import type {
     RemoveCommandSetAndId,
     EventPipeCommandCollectTracing2,
index 9d55897..6180834 100644 (file)
@@ -4,9 +4,9 @@
 /// <reference lib="webworker" />
 
 import monoDiagnosticsMock from "consts:monoDiagnosticsMock";
-import { assertNever } from "../../types";
+import { PromiseAndController, assertNever } from "../../types/internal";
 import { pthread_self } from "../../pthreads/worker";
-import { Module } from "../../globals";
+import { Module, createPromiseController } from "../../globals";
 import cwraps from "../../cwraps";
 import { EventPipeSessionIDImpl } from "../shared/types";
 import { CharPtr } from "../../types/emscripten";
@@ -17,7 +17,6 @@ import {
 
 import { importAndInstantiateMock } from "./mock-remote";
 import type { Mock, MockRemoteSocket } from "../mock";
-import { PromiseAndController, createPromiseController } from "../../promise-controller";
 import {
     isEventPipeCommand,
     isProcessCommand,
index 8f83d12..0a4af35 100644 (file)
@@ -3,7 +3,6 @@
 
 import Serializer from "./base-serializer";
 import { CommandSetId, ServerCommandId } from "./types";
-import { mono_assert } from "../../../types";
 
 export function createBinaryCommandOKReply(payload?: Uint8Array): Uint8Array {
     const len = Serializer.computeMessageByteLength(payload);
index f30b991..97fe380 100644 (file)
@@ -2,9 +2,9 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import monoDiagnosticsMock from "consts:monoDiagnosticsMock";
-import { runtimeHelpers } from "../../globals";
 import type { Mock } from "../mock";
 import { mock } from "../mock";
+import { runtimeHelpers } from "../../globals";
 
 export function importAndInstantiateMock(mockURL: string): Promise<Mock> {
     if (monoDiagnosticsMock) {
index e66d24a..ffa8ce4 100644 (file)
@@ -9,7 +9,7 @@ import {
 } from "./ipc-protocol/types";
 import Magic from "./ipc-protocol/magic";
 import Parser from "./ipc-protocol/base-parser";
-import { assertNever } from "../../types";
+import { assertNever } from "../../types/internal";
 
 export const dotnetDiagnosticsServerProtocolCommandEvent = "dotnet:diagnostics:protocolCommand" as const;
 
index cc3a8d8..c2f8aae 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { assertNever } from "../../types";
+import { assertNever } from "../../types/internal";
 import { VoidPtr } from "../../types/emscripten";
 import { Module } from "../../globals";
 import type { CommonSocket } from "./common-socket";
index 3dcc010..ba28603 100644 (file)
@@ -159,10 +159,6 @@ type MONOType = {
      */
     mono_wasm_load_data_archive: (data: Uint8Array, prefix: string) => boolean;
     /**
-     * @deprecated Please use configSrc instead
-     */
-    mono_wasm_load_config: (configFilePath: string) => Promise<void>;
-    /**
      * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead.
      */
     mono_wasm_new_root_buffer: (capacity: number, name?: string) => WasmRootBuffer;
index 8b8f2e9..d5cbed9 100644 (file)
@@ -139,6 +139,10 @@ type MonoConfig = {
      * application environment
      */
     applicationEnvironment?: string;
+    /**
+     * query string to be used for asset loading
+     */
+    assetUniqueQuery?: string;
 };
 interface ResourceRequest {
     name: string;
@@ -179,7 +183,7 @@ interface AssetEntry extends ResourceRequest {
      */
     pendingDownload?: LoadingResource;
 }
-type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads" | "symbols";
+type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads" | "js-module-runtime" | "js-module-dotnet" | "js-module-native" | "symbols";
 type GlobalizationMode = "icu" | // load ICU globalization data from any runtime assets with behavior "icu".
 "invariant" | //  operate in invariant globalization mode.
 "hybrid" | // operate in hybrid globalization mode with small ICU files, using native platform functions
@@ -252,6 +256,35 @@ type ModuleAPI = {
 };
 type CreateDotnetRuntimeType = (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) => Promise<RuntimeAPI>;
 
+interface IDisposable {
+    dispose(): void;
+    get isDisposed(): boolean;
+}
+interface IMemoryView extends IDisposable {
+    /**
+     * copies elements from provided source to the wasm memory.
+     * target has to have the elements of the same type as the underlying C# array.
+     * same as TypedArray.set()
+     */
+    set(source: TypedArray, targetOffset?: number): void;
+    /**
+     * copies elements from wasm memory to provided target.
+     * target has to have the elements of the same type as the underlying C# array.
+     */
+    copyTo(target: TypedArray, sourceOffset?: number): void;
+    /**
+     * same as TypedArray.slice()
+     */
+    slice(start?: number, end?: number): TypedArray;
+    get length(): number;
+    get byteLength(): number;
+}
+
+declare function mono_exit(exit_code: number, reason?: any): void;
+
+declare const dotnet: DotnetHostBuilder;
+declare const exit: typeof mono_exit;
+
 interface BootJsonData {
     readonly entryAssembly: string;
     readonly resources: ResourceGroups;
@@ -297,35 +330,6 @@ declare enum ICUDataMode {
     Custom = 3
 }
 
-interface IDisposable {
-    dispose(): void;
-    get isDisposed(): boolean;
-}
-interface IMemoryView extends IDisposable {
-    /**
-     * copies elements from provided source to the wasm memory.
-     * target has to have the elements of the same type as the underlying C# array.
-     * same as TypedArray.set()
-     */
-    set(source: TypedArray, targetOffset?: number): void;
-    /**
-     * copies elements from wasm memory to provided target.
-     * target has to have the elements of the same type as the underlying C# array.
-     */
-    copyTo(target: TypedArray, sourceOffset?: number): void;
-    /**
-     * same as TypedArray.slice()
-     */
-    slice(start?: number, end?: number): TypedArray;
-    get length(): number;
-    get byteLength(): number;
-}
-
-declare function mono_exit(exit_code: number, reason?: any): void;
-
-declare const dotnet: DotnetHostBuilder;
-declare const exit: typeof mono_exit;
-
 declare global {
     function getDotnetRuntime(runtimeId: number): RuntimeAPI | undefined;
 }
index 7c42d93..510b2be 100644 (file)
@@ -1,6 +1,2 @@
 
-var fetch = fetch || undefined; var require = require || undefined; var __dirname = __dirname || '';
-var createEmscripten = createDotnetRuntime;
-var unifyModuleConfig = __dotnet_runtime.unifyModuleConfig;
-export const dotnet = __dotnet_runtime.dotnet;
-export const exit = __dotnet_runtime.exit;
\ No newline at end of file
+var fetch = fetch || undefined; var require = require || undefined; var __dirname = __dirname || ''; var _nativeModuleLoaded = false;
index b23d198..68a8899 100644 (file)
@@ -11,7 +11,6 @@ const DISABLE_LEGACY_JS_INTEROP = process.env.DISABLE_LEGACY_JS_INTEROP === "1";
 function setup(disableLegacyJsInterop) {
     const pthreadReplacements = {};
     const dotnet_replacements = {
-        scriptUrl: import.meta.url,
         fetch: globalThis.fetch,
         require,
         updateMemoryViews,
@@ -27,30 +26,21 @@ function setup(disableLegacyJsInterop) {
     #else
     const ENVIRONMENT_IS_PTHREAD = false;
     #endif
-    if (ENVIRONMENT_IS_NODE) {
-        dotnet_replacements.requirePromise = import(/* webpackIgnore: true */'module').then(mod => mod.createRequire(import.meta.url));
-        dotnet_replacements.requirePromise.then(someRequire => {
-            require = someRequire;
-        });
-    }
 
-    __dotnet_runtime.passEmscriptenInternals({
-        isWorker: ENVIRONMENT_IS_WORKER,
-        isShell: ENVIRONMENT_IS_SHELL,
+    Module.__dotnet_runtime.passEmscriptenInternals({
         isPThread: ENVIRONMENT_IS_PTHREAD,
         disableLegacyJsInterop,
         quit_, ExitStatus
     });
+    Module.__dotnet_runtime.initializeReplacements(dotnet_replacements);
 
     #if USE_PTHREADS
     if (ENVIRONMENT_IS_PTHREAD) {
         Module.config = {};
-        __dotnet_runtime.initializeReplacements(dotnet_replacements);
-        __dotnet_runtime.configureWorkerStartup(Module);
+        Module.__dotnet_runtime.configureWorkerStartup(Module);
     } else {
         #endif
-        __dotnet_runtime.initializeReplacements(dotnet_replacements);
-        __dotnet_runtime.configureEmscriptenStartup(Module);
+        Module.__dotnet_runtime.configureEmscriptenStartup(Module);
         #if USE_PTHREADS
     }
     #endif
@@ -58,6 +48,7 @@ function setup(disableLegacyJsInterop) {
     updateMemoryViews = dotnet_replacements.updateMemoryViews;
     noExitRuntime = dotnet_replacements.noExitRuntime;
     fetch = dotnet_replacements.fetch;
+    require = dotnet_replacements.require;
     _scriptDir = __dirname = scriptDirectory = dotnet_replacements.scriptDirectory;
     #if USE_PTHREADS
     PThread.loadWasmModuleToWorker = pthreadReplacements.loadWasmModuleToWorker;
index 3ecc1bc..9eb9b1c 100644 (file)
@@ -1 +1,3 @@
-createDotnetRuntime = Module = unifyModuleConfig(Module, createDotnetRuntime);
\ No newline at end of file
+if (_nativeModuleLoaded) throw new Error("Native module already loaded");
+_nativeModuleLoaded = true;
+createDotnetRuntime = Module = createDotnetRuntime(Module);
\ No newline at end of file
index e258bea..68b5a4f 100644 (file)
@@ -1,14 +1,14 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import type { MonoConfig, APIType } from "./types-api";
+import type { MonoConfig, APIType } from "./types";
 
-import { runtimeHelpers } from "./globals";
 import { mono_wasm_get_assembly_exports } from "./invoke-cs";
 import { mono_wasm_set_module_imports } from "./invoke-js";
 import { getB32, getF32, getF64, getI16, getI32, getI52, getI64Big, getI8, getU16, getU32, getU52, getU8, setB32, setF32, setF64, setI16, setI32, setI52, setI64Big, setI8, setU16, setU32, setU52, setU8 } from "./memory";
 import { mono_run_main, mono_run_main_and_exit } from "./run";
 import { mono_wasm_setenv } from "./startup";
+import { runtimeHelpers } from "./globals";
 
 export function export_api(): any {
     const api: APIType = {
index c7ea641..407c14c 100644 (file)
@@ -5,34 +5,33 @@ import ProductVersion from "consts:productVersion";
 import GitHash from "consts:gitHash";
 import BuildConfiguration from "consts:configuration";
 import WasmEnableLegacyJsInterop from "consts:WasmEnableLegacyJsInterop";
-import type { DotnetHostBuilder, RuntimeAPI } from "./types-api";
+import type { RuntimeAPI } from "./types";
 
-import { Module, disableLegacyJsInterop, exportedRuntimeAPI, passEmscriptenInternals, } from "./globals";
-import { is_nullish } from "./types";
+import { Module, disableLegacyJsInterop, exportedRuntimeAPI, passEmscriptenInternals, runtimeHelpers, setRuntimeGlobals, } from "./globals";
+import { GlobalObjects, is_nullish } from "./types/internal";
 import { configureEmscriptenStartup, configureWorkerStartup } from "./startup";
 
 import { create_weak_ref } from "./weak-ref";
 import { export_internal } from "./exports-internal";
 import { export_api } from "./export-api";
-import { mono_exit } from "./run";
-import { globalObjectsRoot, unifyModuleConfig } from "./run-outer";
-import { HostBuilder } from "./run-outer";
-import { initializeReplacements, init_polyfills } from "./polyfills";
+import { initializeReplacements } from "./polyfills";
 
 // legacy
 import { mono_bind_static_method } from "./net6-legacy/method-calls";
 import { export_binding_api, export_internal_api, export_mono_api } from "./net6-legacy/exports-legacy";
 import { initializeLegacyExports } from "./net6-legacy/globals";
+import { mono_wasm_stringify_as_error_with_stack } from "./logging";
+import { instantiate_asset, instantiate_symbols_asset } from "./assets";
+import { jiterpreter_dump_stats } from "./jiterpreter";
 
-function initializeExports(): RuntimeAPI {
+function initializeExports(globalObjects: GlobalObjects): RuntimeAPI {
     const module = Module;
-    const globals = globalObjectsRoot;
+    const globals = globalObjects;
     const globalThisAny = globalThis as any;
 
     if (WasmEnableLegacyJsInterop && !disableLegacyJsInterop) {
         initializeLegacyExports(globals);
     }
-    init_polyfills();
 
     // here we merge methods from the local objects into exported objects
     if (WasmEnableLegacyJsInterop && !disableLegacyJsInterop) {
@@ -41,6 +40,13 @@ function initializeExports(): RuntimeAPI {
         Object.assign(globals.internal, export_internal_api());
     }
     Object.assign(globals.internal, export_internal());
+    Object.assign(runtimeHelpers, {
+        stringify_as_error_with_stack: mono_wasm_stringify_as_error_with_stack,
+        instantiate_symbols_asset,
+        instantiate_asset,
+        jiterpreter_dump_stats,
+    });
+
     const API = export_api();
     Object.assign(exportedRuntimeAPI, {
         INTERNAL: globals.internal,
@@ -135,9 +141,6 @@ class RuntimeList {
 }
 
 // export external API
-const dotnet: DotnetHostBuilder = new HostBuilder();
-const exit = mono_exit;
 export {
-    dotnet, exit,
-    globalObjectsRoot as earlyExports, passEmscriptenInternals, initializeExports, initializeReplacements, unifyModuleConfig, configureEmscriptenStartup, configureWorkerStartup
+    passEmscriptenInternals, initializeExports, initializeReplacements, configureEmscriptenStartup, configureWorkerStartup, setRuntimeGlobals
 };
\ No newline at end of file
index b1bc580..500b462 100644 (file)
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import { runtimeHelpers } from "./globals";
-import { GCHandle, GCHandleNull, JSHandle, JSHandleDisposed, JSHandleNull, mono_assert } from "./types";
+import { GCHandle, GCHandleNull, JSHandle, JSHandleDisposed, JSHandleNull } from "./types/internal";
 import { create_weak_ref } from "./weak-ref";
 
 const _use_finalization_registry = typeof globalThis.FinalizationRegistry === "function";
index 1f032e3..c3523c1 100644 (file)
@@ -5,66 +5,69 @@
 /// <reference path="./types/v8.d.ts" />
 /// <reference path="./types/node.d.ts" />
 
-import { RuntimeAPI } from "./types-api";
-import type { DotnetModule, GlobalObjects, EmscriptenInternals, EmscriptenModuleInternal, RuntimeHelpers } from "./types";
-import type { EmscriptenModule } from "./types/emscripten";
+import { RuntimeAPI } from "./types/index";
+import type { GlobalObjects, EmscriptenInternals, RuntimeHelpers, LoaderHelpers, DotnetModuleInternal, PromiseAndController } from "./types/internal";
 
 // these are our public API (except internal)
-export let Module: EmscriptenModule & DotnetModule & EmscriptenModuleInternal;
+export let Module: DotnetModuleInternal;
 export let INTERNAL: any;
 
-// these are imported and re-exported from emscripten internals
 export const ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string";
 export const ENVIRONMENT_IS_WEB = typeof window == "object";
-export let ENVIRONMENT_IS_SHELL: boolean;
-export let ENVIRONMENT_IS_WORKER: boolean;
+export const ENVIRONMENT_IS_WORKER = typeof importScripts == "function";
+export const ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
+// these are imported and re-exported from emscripten internals
 export let ENVIRONMENT_IS_PTHREAD: boolean;
 export let exportedRuntimeAPI: RuntimeAPI = null as any;
 export let runtimeHelpers: RuntimeHelpers = null as any;
+export let loaderHelpers: LoaderHelpers = null as any;
 // this is when we link with workload tools. The consts:WasmEnableLegacyJsInterop is when we compile with rollup.
 export let disableLegacyJsInterop = false;
-export let earlyExports: GlobalObjects;
+export let _runtimeModuleLoaded = false; // please keep it in place also as rollup guard
 
-// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function passEmscriptenInternals(
-    internals: EmscriptenInternals,
-): void {
-    ENVIRONMENT_IS_SHELL = internals.isShell;
-    ENVIRONMENT_IS_WORKER = internals.isWorker;
+export function passEmscriptenInternals(internals: EmscriptenInternals): void {
     ENVIRONMENT_IS_PTHREAD = internals.isPThread;
     disableLegacyJsInterop = internals.disableLegacyJsInterop;
     runtimeHelpers.quit = internals.quit_;
     runtimeHelpers.ExitStatus = internals.ExitStatus;
 }
 
-export function setGlobalObjects(
-    globalObjects: GlobalObjects,
-) {
-    earlyExports = globalObjects;
+export function setRuntimeGlobals(globalObjects: GlobalObjects) {
+    if (_runtimeModuleLoaded) {
+        throw new Error("Runtime module already loaded");
+    }
+    _runtimeModuleLoaded = true;
     Module = globalObjects.module;
     INTERNAL = globalObjects.internal;
-    runtimeHelpers = globalObjects.helpers;
+    runtimeHelpers = globalObjects.runtimeHelpers;
+    loaderHelpers = globalObjects.loaderHelpers;
     exportedRuntimeAPI = globalObjects.api;
 
-    Object.assign(globalObjects.module, {
-        disableDotnet6Compatibility: true,
-        config: { environmentVariables: {} }
+    Object.assign(runtimeHelpers, {
+        mono_wasm_bindings_is_ready: false,
+        javaScriptExports: {} as any,
+        enablePerfMeasure: true,
+        allAssetsInMemory: createPromiseController<void>(),
+        dotnetReady: createPromiseController<any>(),
+        memorySnapshotSkippedOrDone: createPromiseController<void>(),
+        afterInstantiateWasm: createPromiseController<void>(),
+        beforePreInit: createPromiseController<void>(),
+        afterPreInit: createPromiseController<void>(),
+        afterPreRun: createPromiseController<void>(),
+        beforeOnRuntimeInitialized: createPromiseController<void>(),
+        afterOnRuntimeInitialized: createPromiseController<void>(),
+        afterPostRun: createPromiseController<void>(),
     });
+
     Object.assign(globalObjects.module.config!, {}) as any;
-    Object.assign(earlyExports.api, {
+    Object.assign(globalObjects.api, {
         Module: globalObjects.module, ...globalObjects.module
     });
-    Object.assign(earlyExports.api, {
-        INTERNAL: earlyExports.internal,
+    Object.assign(globalObjects.api, {
+        INTERNAL: globalObjects.internal,
     });
-    Object.assign(runtimeHelpers, {
-        javaScriptExports: {} as any,
-        mono_wasm_bindings_is_ready: false,
-        maxParallelDownloads: 16,
-        enableDownloadRetry: true,
-        config: globalObjects.module.config,
-        diagnosticTracing: false,
-        enablePerfMeasure: true,
-        loadedFiles: []
-    } as Partial<RuntimeHelpers>);
 }
+
+export function createPromiseController<T>(afterResolve?: () => void, afterReject?: () => void): PromiseAndController<T> {
+    return loaderHelpers.createPromiseController<T>(afterResolve, afterReject);
+}
\ No newline at end of file
index a3d676e..ef0ee24 100644 (file)
@@ -2,9 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import { wrap_as_cancelable_promise } from "./cancelable-promise";
-import { Module, runtimeHelpers } from "./globals";
+import { Module, loaderHelpers } from "./globals";
 import { MemoryViewType, Span } from "./marshal";
-import { mono_assert } from "./types";
 import type { VoidPtr } from "./types/emscripten";
 
 export function http_wasm_supports_streaming_response(): boolean {
@@ -56,7 +55,7 @@ export function http_wasm_fetch(url: string, header_names: string[], header_valu
     }
 
     return wrap_as_cancelable_promise(async () => {
-        const res = await runtimeHelpers.fetch_like(url, options) as ResponseExtension;
+        const res = await loaderHelpers.fetch_like(url, options) as ResponseExtension;
         res.__abort_controller = abort_controller;
         return res;
     });
@@ -66,11 +65,13 @@ function get_response_headers(res: ResponseExtension): void {
     if (!res.__headerNames) {
         res.__headerNames = [];
         res.__headerValues = [];
-        const entries: Iterable<string[]> = (<any>res.headers).entries();
+        if (res.headers && (<any>res.headers).entries) {
+            const entries: Iterable<string[]> = (<any>res.headers).entries();
 
-        for (const pair of entries) {
-            res.__headerNames.push(pair[0]);
-            res.__headerValues.push(pair[1]);
+            for (const pair of entries) {
+                res.__headerNames.push(pair[0]);
+                res.__headerValues.push(pair[1]);
+            }
         }
     }
 }
index 66cc3eb..8e068a6 100644 (file)
@@ -2,14 +2,14 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import { mono_wasm_new_external_root } from "./roots";
-import { MonoString, MonoStringRef } from "./types";
+import { MonoString, MonoStringRef } from "./types/internal";
 import { Int32Ptr } from "./types/emscripten";
 import { conv_string_root, js_string_to_mono_string_root, string_decoder } from "./strings";
 import { setU16_unchecked } from "./memory";
 
-export function mono_wasm_change_case_invariant(exceptionMessage: Int32Ptr, src: number, srcLength: number, dst: number, dstLength: number, toUpper: number) : void{
-    try{
-        const input = string_decoder.decode(<any>src, <any>(src + 2*srcLength));
+export function mono_wasm_change_case_invariant(exceptionMessage: Int32Ptr, src: number, srcLength: number, dst: number, dstLength: number, toUpper: number): void {
+    try {
+        const input = string_decoder.decode(<any>src, <any>(src + 2 * srcLength));
         let result = toUpper ? input.toUpperCase() : input.toLowerCase();
         // Unicode defines some codepoints which expand into multiple codepoints,
         // originally we do not support this expansion
@@ -30,7 +30,7 @@ export function mono_wasm_change_case(exceptionMessage: Int32Ptr, culture: MonoS
         const cultureName = conv_string_root(cultureRoot);
         if (!cultureName)
             throw new Error("Cannot change case, the culture name is null.");
-        const input = string_decoder.decode(<any>src, <any>(src + 2*srcLength));
+        const input = string_decoder.decode(<any>src, <any>(src + 2 * srcLength));
         let result = toUpper ? input.toLocaleUpperCase(cultureName) : input.toLocaleLowerCase(cultureName);
         if (result.length > destLength)
             result = input;
index f3b5b45..28aa01a 100644 (file)
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import cwraps from "./cwraps";
-import { ENVIRONMENT_IS_WEB, Module, runtimeHelpers } from "./globals";
 import { VoidPtr } from "./types/emscripten";
 
 // @offset must be the address of an ICU data archive in the native heap.
@@ -10,70 +9,3 @@ import { VoidPtr } from "./types/emscripten";
 export function mono_wasm_load_icu_data(offset: VoidPtr): boolean {
     return (cwraps.mono_wasm_load_icu_data(offset)) === 1;
 }
-
-export function init_globalization() {
-    runtimeHelpers.invariantMode = runtimeHelpers.config.globalizationMode === "invariant";
-    runtimeHelpers.preferredIcuAsset = get_preferred_icu_asset();
-
-    if (!runtimeHelpers.invariantMode) {
-        if (runtimeHelpers.preferredIcuAsset) {
-            if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: ICU data archive(s) available, disabling invariant mode");
-        } else if (runtimeHelpers.config.globalizationMode !== "icu") {
-            if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: ICU data archive(s) not available, using invariant globalization mode");
-            runtimeHelpers.invariantMode = true;
-            runtimeHelpers.preferredIcuAsset = null;
-        } else {
-            const msg = "invariant globalization mode is inactive and no ICU data archives are available";
-            Module.err(`MONO_WASM: ERROR: ${msg}`);
-            throw new Error(msg);
-        }
-    }
-
-    const invariantEnv = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
-    const hybridEnv = "DOTNET_SYSTEM_GLOBALIZATION_HYBRID";
-    const env_variables = runtimeHelpers.config.environmentVariables!;
-    if (env_variables[hybridEnv] === undefined && runtimeHelpers.config.globalizationMode === "hybrid") {
-        env_variables[hybridEnv] = "1";
-    }
-    else if (env_variables[invariantEnv] === undefined && runtimeHelpers.invariantMode) {
-        env_variables[invariantEnv] = "1";
-    }
-    if (env_variables["TZ"] === undefined) {
-        try {
-            // this call is relatively expensive, so we call it during download of other assets
-            const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || null;
-            if (timezone) {
-                env_variables!["TZ"] = timezone;
-            }
-        } catch {
-            console.info("MONO_WASM: failed to detect timezone, will fallback to UTC");
-        }
-    }
-}
-
-export function get_preferred_icu_asset(): string | null {
-    if (!runtimeHelpers.config.assets || runtimeHelpers.invariantMode)
-        return null;
-
-    // By setting <WasmIcuDataFileName> user can define what ICU source file they want to load.
-    // There is no need to check application's culture when <WasmIcuDataFileName> is set.
-    // If it was not set, then we have 3 "icu" assets in config and we should choose
-    // only one for loading, the one that matches the application's locale.
-    const icuAssets = runtimeHelpers.config.assets.filter(a => a["behavior"] == "icu");
-    if (icuAssets.length === 1)
-        return icuAssets[0].name;
-
-    // reads the browsers locale / the OS's locale
-    const preferredCulture = ENVIRONMENT_IS_WEB ? navigator.language : Intl.DateTimeFormat().resolvedOptions().locale;
-    const prefix = preferredCulture.split("-")[0];
-    const CJK = "icudt_CJK.dat";
-    const EFIGS = "icudt_EFIGS.dat";
-    const OTHERS = "icudt_no_CJK.dat";
-
-    // not all "fr-*", "it-*", "de-*", "es-*" are in EFIGS, only the one that is mostly used
-    if (prefix == "en" || ["fr", "fr-FR", "it", "it-IT", "de", "de-DE", "es", "es-ES"].includes(preferredCulture))
-        return EFIGS;
-    if (["zh", "ko", "ja"].includes(prefix))
-        return CJK;
-    return OTHERS;
-}
index 340b3af..5ab7d46 100644 (file)
@@ -11,7 +11,7 @@ import {
 } from "./marshal";
 import { mono_wasm_new_external_root, mono_wasm_new_root } from "./roots";
 import { conv_string, conv_string_root } from "./strings";
-import { mono_assert, MonoObjectRef, MonoStringRef, MonoString, MonoObject, MonoMethod, JSMarshalerArguments, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, VoidPtrNull, MonoObjectRefNull, MonoObjectNull } from "./types";
+import { MonoObjectRef, MonoStringRef, MonoString, MonoObject, MonoMethod, JSMarshalerArguments, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, VoidPtrNull, MonoObjectRefNull, MonoObjectNull } from "./types/internal";
 import { Int32Ptr } from "./types/emscripten";
 import cwraps from "./cwraps";
 import { assembly_load } from "./class-loader";
index 6cb566f..4080ff1 100644 (file)
@@ -5,7 +5,7 @@ import { marshal_exception_to_cs, bind_arg_marshal_to_cs } from "./marshal-to-cs
 import { get_signature_argument_count, bound_js_function_symbol, get_sig, get_signature_version, get_signature_type, imported_js_function_symbol } from "./marshal";
 import { setI32_unchecked } from "./memory";
 import { conv_string_root, js_string_to_mono_string_root } from "./strings";
-import { mono_assert, MonoObject, MonoObjectRef, MonoString, MonoStringRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType } from "./types";
+import { MonoObject, MonoObjectRef, MonoString, MonoStringRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType } from "./types/internal";
 import { Int32Ptr } from "./types/emscripten";
 import { INTERNAL, Module, runtimeHelpers } from "./globals";
 import { bind_arg_marshal_to_js } from "./marshal-to-js";
index ed94dc0..36c016b 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { mono_assert, MonoMethod, MonoType } from "./types";
+import { MonoMethod, MonoType } from "./types/internal";
 import { NativePointer } from "./types/emscripten";
 import { Module } from "./globals";
 import {
index b1bf6a5..ef349c6 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { mono_assert, MonoType, MonoMethod } from "./types";
+import { MonoType, MonoMethod } from "./types/internal";
 import { NativePointer, Int32Ptr, VoidPtr } from "./types/emscripten";
 import { Module, runtimeHelpers } from "./globals";
 import {
index bc4df33..dfecf49 100644 (file)
@@ -1,7 +1,6 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { mono_assert } from "./types";
 import { NativePointer, ManagedPointer, VoidPtr } from "./types/emscripten";
 import { Module, runtimeHelpers } from "./globals";
 import { WasmOpcode, WasmSimdOpcode } from "./jiterpreter-opcodes";
index 11ec48a..5243e11 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { mono_assert, MonoMethod } from "./types";
+import { MonoMethod } from "./types/internal";
 import { Module } from "./globals";
 import { NativePointer } from "./types/emscripten";
 import {
@@ -44,25 +44,25 @@ import {
 
 /*
 struct MonoVTable {
-       MonoClass  *klass; // 0
-       MonoGCDescriptor gc_descr; // 4
-       MonoDomain *domain; // 8
-       gpointer    type; // 12
-       guint8     *interface_bitmap; // 16
-       guint32     max_interface_id; // 20
-       guint8      rank; // 21
-       guint8      initialized; // 22
-       guint8      flags;
+    MonoClass  *klass; // 0
+    MonoGCDescriptor gc_descr; // 4
+    MonoDomain *domain; // 8
+    gpointer    type; // 12
+    guint8     *interface_bitmap; // 16
+    guint32     max_interface_id; // 20
+    guint8      rank; // 21
+    guint8      initialized; // 22
+    guint8      flags;
 */
 
 /*
 struct InterpFrame {
-       InterpFrame    *parent; // 0
-       InterpMethod   *imethod; // 4
-       stackval       *retval; // 8
-       stackval       *stack; // 12
-       InterpFrame    *next_free; // 16
-       InterpState state; // 20
+    InterpFrame    *parent; // 0
+    InterpMethod   *imethod; // 4
+    stackval       *retval; // 8
+    stackval       *stack; // 12
+    InterpFrame    *next_free; // 16
+    InterpState state; // 20
 };
 
 struct InterpMethod {
@@ -87,55 +87,55 @@ const enum JiterpSpecialOpcode {
 }
 
 // indexPlusOne so that ip[1] in the interpreter becomes getArgU16(ip, 1)
-function getArgU16 (ip: MintOpcodePtr, indexPlusOne: number) {
+function getArgU16(ip: MintOpcodePtr, indexPlusOne: number) {
     return getU16(<any>ip + (2 * indexPlusOne));
 }
 
-function getArgI16 (ip: MintOpcodePtr, indexPlusOne: number) {
+function getArgI16(ip: MintOpcodePtr, indexPlusOne: number) {
     return getI16(<any>ip + (2 * indexPlusOne));
 }
 
-function getArgI32 (ip: MintOpcodePtr, indexPlusOne: number) {
+function getArgI32(ip: MintOpcodePtr, indexPlusOne: number) {
     const src = <any>ip + (2 * indexPlusOne);
     return getI32_unaligned(src);
 }
 
-function getArgU32 (ip: MintOpcodePtr, indexPlusOne: number) {
+function getArgU32(ip: MintOpcodePtr, indexPlusOne: number) {
     const src = <any>ip + (2 * indexPlusOne);
     return getU32_unaligned(src);
 }
 
-function getArgF32 (ip: MintOpcodePtr, indexPlusOne: number) {
+function getArgF32(ip: MintOpcodePtr, indexPlusOne: number) {
     const src = <any>ip + (2 * indexPlusOne);
     return getF32_unaligned(src);
 }
 
-function getArgF64 (ip: MintOpcodePtr, indexPlusOne: number) {
+function getArgF64(ip: MintOpcodePtr, indexPlusOne: number) {
     const src = <any>ip + (2 * indexPlusOne);
     return getF64_unaligned(src);
 }
 
-function get_imethod (frame: NativePointer) {
+function get_imethod(frame: NativePointer) {
     // FIXME: Encoding this data directly into the trace will prevent trace reuse
     const iMethod = getU32_unaligned(<any>frame + getMemberOffset(JiterpMember.Imethod));
     return iMethod;
 }
 
-function get_imethod_data (frame: NativePointer, index: number) {
+function get_imethod_data(frame: NativePointer, index: number) {
     // FIXME: Encoding this data directly into the trace will prevent trace reuse
     const pData = getU32_unaligned(get_imethod(frame) + getMemberOffset(JiterpMember.DataItems));
     const dataOffset = pData + (index * sizeOfDataItem);
     return getU32_unaligned(dataOffset);
 }
 
-function get_imethod_clause_data_offset (frame: NativePointer, index: number) {
+function get_imethod_clause_data_offset(frame: NativePointer, index: number) {
     // FIXME: Encoding this data directly into the trace will prevent trace reuse
     const pData = getU32_unaligned(get_imethod(frame) + getMemberOffset(JiterpMember.ClauseDataOffsets));
     const dataOffset = pData + (index * sizeOfDataItem);
     return getU32_unaligned(dataOffset);
 }
 
-function is_backward_branch_target (
+function is_backward_branch_target(
     ip: MintOpcodePtr, startOfBody: MintOpcodePtr,
     backwardBranchTable: Uint16Array | null
 ) {
@@ -153,16 +153,16 @@ function is_backward_branch_target (
 
 const knownConstantValues = new Map<number, number>();
 
-function get_known_constant_value (localOffset: number) : number | undefined {
+function get_known_constant_value(localOffset: number): number | undefined {
     return knownConstantValues.get(localOffset);
 }
 
-export function generateWasmBody (
+export function generateWasmBody(
     frame: NativePointer, traceName: string, ip: MintOpcodePtr,
     startOfBody: MintOpcodePtr, endOfBody: MintOpcodePtr,
     builder: WasmBuilder, instrumentedTraceId: number,
     backwardBranchTable: Uint16Array | null
-) : number {
+): number {
     const abort = <MintOpcodePtr><any>0;
     let isFirstInstruction = true, isConditionallyExecuted = false,
         firstOpcodeInBlock = true, containsSimd = false,
@@ -388,10 +388,10 @@ export function generateWasmBody (
                 const sizeOffset = getArgU16(ip, 3),
                     valueOffset = getArgU16(ip, 2),
                     destOffset = getArgU16(ip, 1);
-                    /*
-                    constantSize = get_known_constant_value(sizeOffset),
-                    constantValue = get_known_constant_value(valueOffset);
-                    */
+                /*
+                constantSize = get_known_constant_value(sizeOffset),
+                constantValue = get_known_constant_value(valueOffset);
+                */
 
                 // TODO: Handle constant size initblks. Not sure if they matter though
                 // FIXME: This will cause an erroneous bailout if dest and size are both 0
@@ -1108,7 +1108,7 @@ export function generateWasmBody (
             case MintOpcode.MINT_CONV_I8_R4:
             case MintOpcode.MINT_CONV_I8_R8: {
                 const isF32 = (opcode === MintOpcode.MINT_CONV_I4_R4) ||
-                        (opcode === MintOpcode.MINT_CONV_I8_R4),
+                    (opcode === MintOpcode.MINT_CONV_I8_R4),
                     isI64 = (opcode === MintOpcode.MINT_CONV_I8_R4) ||
                         (opcode === MintOpcode.MINT_CONV_I8_R8),
                     limit = isI64
@@ -1446,32 +1446,32 @@ export function generateWasmBody (
     return result;
 }
 
-const notNullSince : Map<number, number> = new Map();
+const notNullSince: Map<number, number> = new Map();
 let cknullOffset = -1;
 
-function eraseInferredState () {
+function eraseInferredState() {
     cknullOffset = -1;
     notNullSince.clear();
     knownConstantValues.clear();
 }
 
-function invalidate_local (offset: number) {
+function invalidate_local(offset: number) {
     if (cknullOffset === offset)
         cknullOffset = -1;
     notNullSince.delete(offset);
     knownConstantValues.delete(offset);
 }
 
-function invalidate_local_range (start: number, bytes: number) {
+function invalidate_local_range(start: number, bytes: number) {
     for (let i = 0; i < bytes; i += 1)
         invalidate_local(start + i);
 }
 
-function append_branch_target_block (builder: WasmBuilder, ip: MintOpcodePtr, isBackBranchTarget: boolean) {
+function append_branch_target_block(builder: WasmBuilder, ip: MintOpcodePtr, isBackBranchTarget: boolean) {
     builder.cfg.startBranchBlock(ip, isBackBranchTarget);
 }
 
-function append_ldloc (builder: WasmBuilder, offset: number, opcodeOrPrefix: WasmOpcode, simdOpcode?: WasmSimdOpcode) {
+function append_ldloc(builder: WasmBuilder, offset: number, opcodeOrPrefix: WasmOpcode, simdOpcode?: WasmSimdOpcode) {
     builder.local("pLocals");
     builder.appendU8(opcodeOrPrefix);
     if (simdOpcode !== undefined) {
@@ -1489,7 +1489,7 @@ function append_ldloc (builder: WasmBuilder, offset: number, opcodeOrPrefix: Was
 //  where the offset+alignment pair is referred to as a 'memarg' by the spec.
 // The actual store operation is equivalent to `pBase[offset] = value` (alignment has no
 //  observable impact on behavior, other than causing compilation failures if out of range)
-function append_stloc_tail (builder: WasmBuilder, offset: number, opcodeOrPrefix: WasmOpcode, simdOpcode?: WasmSimdOpcode) {
+function append_stloc_tail(builder: WasmBuilder, offset: number, opcodeOrPrefix: WasmOpcode, simdOpcode?: WasmSimdOpcode) {
     builder.appendU8(opcodeOrPrefix);
     if (simdOpcode !== undefined) {
         // This looks wrong but I assure you it's correct.
@@ -1506,7 +1506,7 @@ function append_stloc_tail (builder: WasmBuilder, offset: number, opcodeOrPrefix
 //  used for writes
 // Pass transient=true if the address will not persist after use (so it can't be used to later
 //  modify the contents of this local)
-function append_ldloca (builder: WasmBuilder, localOffset: number, bytesInvalidated?: number) {
+function append_ldloca(builder: WasmBuilder, localOffset: number, bytesInvalidated?: number) {
     if (typeof (bytesInvalidated) !== "number")
         bytesInvalidated = 512;
     // FIXME: We need to know how big this variable is so we can invalidate the whole space it occupies
@@ -1515,7 +1515,7 @@ function append_ldloca (builder: WasmBuilder, localOffset: number, bytesInvalida
     builder.lea("pLocals", localOffset);
 }
 
-function append_memset_local (builder: WasmBuilder, localOffset: number, value: number, count: number) {
+function append_memset_local(builder: WasmBuilder, localOffset: number, value: number, count: number) {
     invalidate_local_range(localOffset, count);
 
     // spec: pop n, pop val, pop d, fill from d[0] to d[n] with value val
@@ -1527,7 +1527,7 @@ function append_memset_local (builder: WasmBuilder, localOffset: number, value:
     append_memset_dest(builder, value, count);
 }
 
-function append_memmove_local_local (builder: WasmBuilder, destLocalOffset: number, sourceLocalOffset: number, count: number) {
+function append_memmove_local_local(builder: WasmBuilder, destLocalOffset: number, sourceLocalOffset: number, count: number) {
     invalidate_local_range(destLocalOffset, count);
 
     if (try_append_memmove_fast(builder, destLocalOffset, sourceLocalOffset, count, false))
@@ -1539,12 +1539,12 @@ function append_memmove_local_local (builder: WasmBuilder, destLocalOffset: numb
     append_memmove_dest_src(builder, count);
 }
 
-function isAddressTaken (builder: WasmBuilder, localOffset: number) {
+function isAddressTaken(builder: WasmBuilder, localOffset: number) {
     return cwraps.mono_jiterp_is_imethod_var_address_taken(<any>get_imethod(builder.frame), localOffset) !== 0;
 }
 
 // Loads the specified i32 value and then bails out if it is null, leaving it in the cknull_ptr local.
-function append_ldloc_cknull (builder: WasmBuilder, localOffset: number, ip: MintOpcodePtr, leaveOnStack: boolean) {
+function append_ldloc_cknull(builder: WasmBuilder, localOffset: number, ip: MintOpcodePtr, leaveOnStack: boolean) {
     const optimize = builder.allowNullCheckOptimization &&
         notNullSince.has(localOffset) &&
         !isAddressTaken(builder, localOffset);
@@ -1597,22 +1597,22 @@ function append_ldloc_cknull (builder: WasmBuilder, localOffset: number, ip: Min
         cknullOffset = -1;
 }
 
-const ldcTable : { [opcode: number]: [WasmOpcode, number] } = {
+const ldcTable: { [opcode: number]: [WasmOpcode, number] } = {
     [MintOpcode.MINT_LDC_I4_M1]: [WasmOpcode.i32_const, -1],
-    [MintOpcode.MINT_LDC_I4_0]:  [WasmOpcode.i32_const, 0 ],
-    [MintOpcode.MINT_LDC_I4_1]:  [WasmOpcode.i32_const, 1 ],
-    [MintOpcode.MINT_LDC_I4_2]:  [WasmOpcode.i32_const, 2 ],
-    [MintOpcode.MINT_LDC_I4_3]:  [WasmOpcode.i32_const, 3 ],
-    [MintOpcode.MINT_LDC_I4_4]:  [WasmOpcode.i32_const, 4 ],
-    [MintOpcode.MINT_LDC_I4_5]:  [WasmOpcode.i32_const, 5 ],
-    [MintOpcode.MINT_LDC_I4_6]:  [WasmOpcode.i32_const, 6 ],
-    [MintOpcode.MINT_LDC_I4_7]:  [WasmOpcode.i32_const, 7 ],
-    [MintOpcode.MINT_LDC_I4_8]:  [WasmOpcode.i32_const, 8 ],
+    [MintOpcode.MINT_LDC_I4_0]: [WasmOpcode.i32_const, 0],
+    [MintOpcode.MINT_LDC_I4_1]: [WasmOpcode.i32_const, 1],
+    [MintOpcode.MINT_LDC_I4_2]: [WasmOpcode.i32_const, 2],
+    [MintOpcode.MINT_LDC_I4_3]: [WasmOpcode.i32_const, 3],
+    [MintOpcode.MINT_LDC_I4_4]: [WasmOpcode.i32_const, 4],
+    [MintOpcode.MINT_LDC_I4_5]: [WasmOpcode.i32_const, 5],
+    [MintOpcode.MINT_LDC_I4_6]: [WasmOpcode.i32_const, 6],
+    [MintOpcode.MINT_LDC_I4_7]: [WasmOpcode.i32_const, 7],
+    [MintOpcode.MINT_LDC_I4_8]: [WasmOpcode.i32_const, 8],
 };
 
-function emit_ldc (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode) : boolean {
+function emit_ldc(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean {
     let storeType = WasmOpcode.i32_store;
-    let value : number | undefined;
+    let value: number | undefined;
 
     const tableEntry = ldcTable[opcode];
     if (tableEntry) {
@@ -1682,7 +1682,7 @@ function emit_ldc (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode)
     return true;
 }
 
-function emit_mov (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode) : boolean {
+function emit_mov(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean {
     let loadOp = WasmOpcode.i32_load, storeOp = WasmOpcode.i32_store;
     switch (opcode) {
         case MintOpcode.MINT_MOV_I4_I1:
@@ -1745,7 +1745,7 @@ function emit_mov (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode)
     return true;
 }
 
-function append_vtable_initialize (builder: WasmBuilder, pVtable: NativePointer, ip: MintOpcodePtr) {
+function append_vtable_initialize(builder: WasmBuilder, pVtable: NativePointer, ip: MintOpcodePtr) {
     // TODO: Actually initialize the vtable instead of just checking and bailing out?
     builder.block();
     // FIXME: This will prevent us from reusing traces between runs since the vtables can move
@@ -1760,17 +1760,18 @@ function append_vtable_initialize (builder: WasmBuilder, pVtable: NativePointer,
     builder.endBlock();
 }
 
-function emit_fieldop (
+function emit_fieldop(
     builder: WasmBuilder, frame: NativePointer,
     ip: MintOpcodePtr, opcode: MintOpcode
-) : boolean {
+): boolean {
     const isLoad = (
         (opcode >= MintOpcode.MINT_LDFLD_I1) &&
         (opcode <= MintOpcode.MINT_LDFLDA_UNSAFE)
-    ) || (
-        (opcode >= MintOpcode.MINT_LDSFLD_I1) &&
-        (opcode <= MintOpcode.MINT_LDSFLD_W)
-    );
+    ) ||
+        (
+            (opcode >= MintOpcode.MINT_LDSFLD_I1) &&
+            (opcode <= MintOpcode.MINT_LDSFLD_W)
+        );
 
     const objectOffset = getArgU16(ip, isLoad ? 2 : 1),
         fieldOffset = getArgU16(ip, 3),
@@ -1945,17 +1946,18 @@ function emit_fieldop (
     }
 }
 
-function emit_sfieldop (
+function emit_sfieldop(
     builder: WasmBuilder, frame: NativePointer,
     ip: MintOpcodePtr, opcode: MintOpcode
-) : boolean {
+): boolean {
     const isLoad = (
         (opcode >= MintOpcode.MINT_LDFLD_I1) &&
         (opcode <= MintOpcode.MINT_LDFLDA_UNSAFE)
-    ) || (
-        (opcode >= MintOpcode.MINT_LDSFLD_I1) &&
-        (opcode <= MintOpcode.MINT_LDSFLD_W)
-    );
+    ) ||
+        (
+            (opcode >= MintOpcode.MINT_LDSFLD_I1) &&
+            (opcode <= MintOpcode.MINT_LDSFLD_W)
+        );
 
     const localOffset = getArgU16(ip, 1),
         pVtable = get_imethod_data(frame, getArgU16(ip, 2)),
@@ -2056,7 +2058,7 @@ type OpRec3 = [WasmOpcode, WasmOpcode, WasmOpcode];
 // operator, lhsLoadOperator, rhsLoadOperator, storeOperator
 type OpRec4 = [WasmOpcode, WasmOpcode, WasmOpcode, WasmOpcode];
 
-const floatToIntTable : { [opcode: number]: WasmOpcode } = {
+const floatToIntTable: { [opcode: number]: WasmOpcode } = {
     [MintOpcode.MINT_CONV_I4_R4]: WasmOpcode.i32_trunc_s_f32,
     [MintOpcode.MINT_CONV_I8_R4]: WasmOpcode.i64_trunc_s_f32,
     [MintOpcode.MINT_CONV_I4_R8]: WasmOpcode.i32_trunc_s_f64,
@@ -2064,25 +2066,25 @@ const floatToIntTable : { [opcode: number]: WasmOpcode } = {
 };
 
 // thanks for making this as complex as possible, typescript
-const unopTable : { [opcode: number]: OpRec3 | undefined } = {
-    [MintOpcode.MINT_CEQ0_I4]:    [WasmOpcode.i32_eqz,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_ADD1_I4]:    [WasmOpcode.i32_add,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_SUB1_I4]:    [WasmOpcode.i32_sub,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_NEG_I4]:     [WasmOpcode.i32_sub,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_NOT_I4]:     [WasmOpcode.i32_xor,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-
-    [MintOpcode.MINT_ADD1_I8]:    [WasmOpcode.i64_add,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_SUB1_I8]:    [WasmOpcode.i64_sub,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_NEG_I8]:     [WasmOpcode.i64_sub,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_NOT_I8]:     [WasmOpcode.i64_xor,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-
-    [MintOpcode.MINT_ADD_I4_IMM]: [WasmOpcode.i32_add,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_MUL_I4_IMM]: [WasmOpcode.i32_mul,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_ADD_I8_IMM]: [WasmOpcode.i64_add,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_MUL_I8_IMM]: [WasmOpcode.i64_mul,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-
-    [MintOpcode.MINT_NEG_R4]:     [WasmOpcode.f32_neg,   WasmOpcode.f32_load, WasmOpcode.f32_store],
-    [MintOpcode.MINT_NEG_R8]:     [WasmOpcode.f64_neg,   WasmOpcode.f64_load, WasmOpcode.f64_store],
+const unopTable: { [opcode: number]: OpRec3 | undefined } = {
+    [MintOpcode.MINT_CEQ0_I4]: [WasmOpcode.i32_eqz, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_ADD1_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_SUB1_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_NEG_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_NOT_I4]: [WasmOpcode.i32_xor, WasmOpcode.i32_load, WasmOpcode.i32_store],
+
+    [MintOpcode.MINT_ADD1_I8]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_SUB1_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_NEG_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_NOT_I8]: [WasmOpcode.i64_xor, WasmOpcode.i64_load, WasmOpcode.i64_store],
+
+    [MintOpcode.MINT_ADD_I4_IMM]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_MUL_I4_IMM]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_ADD_I8_IMM]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_MUL_I8_IMM]: [WasmOpcode.i64_mul, WasmOpcode.i64_load, WasmOpcode.i64_store],
+
+    [MintOpcode.MINT_NEG_R4]: [WasmOpcode.f32_neg, WasmOpcode.f32_load, WasmOpcode.f32_store],
+    [MintOpcode.MINT_NEG_R8]: [WasmOpcode.f64_neg, WasmOpcode.f64_load, WasmOpcode.f64_store],
 
     [MintOpcode.MINT_CONV_R4_I4]: [WasmOpcode.f32_convert_s_i32, WasmOpcode.i32_load, WasmOpcode.f32_store],
     [MintOpcode.MINT_CONV_R8_I4]: [WasmOpcode.f64_convert_s_i32, WasmOpcode.i32_load, WasmOpcode.f64_store],
@@ -2090,45 +2092,45 @@ const unopTable : { [opcode: number]: OpRec3 | undefined } = {
     [MintOpcode.MINT_CONV_R4_I8]: [WasmOpcode.f32_convert_s_i64, WasmOpcode.i64_load, WasmOpcode.f32_store],
     [MintOpcode.MINT_CONV_R8_I8]: [WasmOpcode.f64_convert_s_i64, WasmOpcode.i64_load, WasmOpcode.f64_store],
     [MintOpcode.MINT_CONV_R_UN_I8]: [WasmOpcode.f64_convert_u_i64, WasmOpcode.i64_load, WasmOpcode.f64_store],
-    [MintOpcode.MINT_CONV_R8_R4]: [WasmOpcode.f64_promote_f32,   WasmOpcode.f32_load, WasmOpcode.f64_store],
-    [MintOpcode.MINT_CONV_R4_R8]: [WasmOpcode.f32_demote_f64,    WasmOpcode.f64_load, WasmOpcode.f32_store],
-
-    [MintOpcode.MINT_CONV_I8_I4]: [WasmOpcode.nop,               WasmOpcode.i64_load32_s, WasmOpcode.i64_store],
-    [MintOpcode.MINT_CONV_I8_U4]: [WasmOpcode.nop,               WasmOpcode.i64_load32_u, WasmOpcode.i64_store],
-
-    [MintOpcode.MINT_CONV_U1_I4]: [WasmOpcode.i32_and,           WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CONV_U2_I4]: [WasmOpcode.i32_and,           WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CONV_I1_I4]: [WasmOpcode.i32_shr_s,         WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CONV_I2_I4]: [WasmOpcode.i32_shr_s,         WasmOpcode.i32_load, WasmOpcode.i32_store],
-
-    [MintOpcode.MINT_CONV_U1_I8]: [WasmOpcode.i32_and,           WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CONV_U2_I8]: [WasmOpcode.i32_and,           WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CONV_I1_I8]: [WasmOpcode.i32_shr_s,           WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CONV_I2_I8]: [WasmOpcode.i32_shr_s,           WasmOpcode.i64_load, WasmOpcode.i32_store],
-
-    [MintOpcode.MINT_SHL_I4_IMM]:     [WasmOpcode.i32_shl,       WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_SHL_I8_IMM]:     [WasmOpcode.i64_shl,       WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_SHR_I4_IMM]:     [WasmOpcode.i32_shr_s,     WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_SHR_I8_IMM]:     [WasmOpcode.i64_shr_s,     WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_SHR_UN_I4_IMM]:  [WasmOpcode.i32_shr_u,     WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_SHR_UN_I8_IMM]:  [WasmOpcode.i64_shr_u,     WasmOpcode.i64_load, WasmOpcode.i64_store],
-
-    [MintOpcode.MINT_ROL_I4_IMM]:     [WasmOpcode.i32_rotl,      WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_ROL_I8_IMM]:     [WasmOpcode.i64_rotl,      WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_ROR_I4_IMM]:     [WasmOpcode.i32_rotr,      WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_ROR_I8_IMM]:     [WasmOpcode.i64_rotr,      WasmOpcode.i64_load, WasmOpcode.i64_store],
-
-    [MintOpcode.MINT_CLZ_I4]:         [WasmOpcode.i32_clz,       WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CTZ_I4]:         [WasmOpcode.i32_ctz,       WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_POPCNT_I4]:      [WasmOpcode.i32_popcnt,    WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CLZ_I8]:         [WasmOpcode.i64_clz,       WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_CTZ_I8]:         [WasmOpcode.i64_ctz,       WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_POPCNT_I8]:      [WasmOpcode.i64_popcnt,    WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_CONV_R8_R4]: [WasmOpcode.f64_promote_f32, WasmOpcode.f32_load, WasmOpcode.f64_store],
+    [MintOpcode.MINT_CONV_R4_R8]: [WasmOpcode.f32_demote_f64, WasmOpcode.f64_load, WasmOpcode.f32_store],
+
+    [MintOpcode.MINT_CONV_I8_I4]: [WasmOpcode.nop, WasmOpcode.i64_load32_s, WasmOpcode.i64_store],
+    [MintOpcode.MINT_CONV_I8_U4]: [WasmOpcode.nop, WasmOpcode.i64_load32_u, WasmOpcode.i64_store],
+
+    [MintOpcode.MINT_CONV_U1_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CONV_U2_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CONV_I1_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CONV_I2_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+
+    [MintOpcode.MINT_CONV_U1_I8]: [WasmOpcode.i32_and, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CONV_U2_I8]: [WasmOpcode.i32_and, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CONV_I1_I8]: [WasmOpcode.i32_shr_s, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CONV_I2_I8]: [WasmOpcode.i32_shr_s, WasmOpcode.i64_load, WasmOpcode.i32_store],
+
+    [MintOpcode.MINT_SHL_I4_IMM]: [WasmOpcode.i32_shl, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_SHL_I8_IMM]: [WasmOpcode.i64_shl, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_SHR_I4_IMM]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_SHR_I8_IMM]: [WasmOpcode.i64_shr_s, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_SHR_UN_I4_IMM]: [WasmOpcode.i32_shr_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_SHR_UN_I8_IMM]: [WasmOpcode.i64_shr_u, WasmOpcode.i64_load, WasmOpcode.i64_store],
+
+    [MintOpcode.MINT_ROL_I4_IMM]: [WasmOpcode.i32_rotl, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_ROL_I8_IMM]: [WasmOpcode.i64_rotl, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_ROR_I4_IMM]: [WasmOpcode.i32_rotr, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_ROR_I8_IMM]: [WasmOpcode.i64_rotr, WasmOpcode.i64_load, WasmOpcode.i64_store],
+
+    [MintOpcode.MINT_CLZ_I4]: [WasmOpcode.i32_clz, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CTZ_I4]: [WasmOpcode.i32_ctz, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_POPCNT_I4]: [WasmOpcode.i32_popcnt, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CLZ_I8]: [WasmOpcode.i64_clz, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_CTZ_I8]: [WasmOpcode.i64_ctz, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_POPCNT_I8]: [WasmOpcode.i64_popcnt, WasmOpcode.i64_load, WasmOpcode.i64_store],
 };
 
 // HACK: Generating correct wasm for these is non-trivial so we hand them off to C.
 // The opcode specifies whether the operands need to be promoted first.
-const intrinsicFpBinops : { [opcode: number] : WasmOpcode } = {
+const intrinsicFpBinops: { [opcode: number]: WasmOpcode } = {
     [MintOpcode.MINT_CEQ_R4]: WasmOpcode.f64_promote_f32,
     [MintOpcode.MINT_CEQ_R8]: WasmOpcode.nop,
     [MintOpcode.MINT_CNE_R4]: WasmOpcode.f64_promote_f32,
@@ -2153,160 +2155,160 @@ const intrinsicFpBinops : { [opcode: number] : WasmOpcode } = {
     [JiterpSpecialOpcode.CNE_UN_R8]: WasmOpcode.nop,
 };
 
-const binopTable : { [opcode: number]: OpRec3 | OpRec4 | undefined } = {
-    [MintOpcode.MINT_ADD_I4]:    [WasmOpcode.i32_add,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_ADD_OVF_I4]:[WasmOpcode.i32_add,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_ADD_OVF_UN_I4]:[WasmOpcode.i32_add,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_SUB_I4]:    [WasmOpcode.i32_sub,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_MUL_I4]:    [WasmOpcode.i32_mul,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_MUL_OVF_I4]:[WasmOpcode.i32_mul,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_MUL_OVF_UN_I4]:[WasmOpcode.i32_mul,WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_DIV_I4]:    [WasmOpcode.i32_div_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+const binopTable: { [opcode: number]: OpRec3 | OpRec4 | undefined } = {
+    [MintOpcode.MINT_ADD_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_ADD_OVF_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_ADD_OVF_UN_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_SUB_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_MUL_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_MUL_OVF_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_MUL_OVF_UN_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_DIV_I4]: [WasmOpcode.i32_div_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
     [MintOpcode.MINT_DIV_UN_I4]: [WasmOpcode.i32_div_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_REM_I4]:    [WasmOpcode.i32_rem_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_REM_I4]: [WasmOpcode.i32_rem_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
     [MintOpcode.MINT_REM_UN_I4]: [WasmOpcode.i32_rem_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_AND_I4]:    [WasmOpcode.i32_and,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_OR_I4]:     [WasmOpcode.i32_or,    WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_XOR_I4]:    [WasmOpcode.i32_xor,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_SHL_I4]:    [WasmOpcode.i32_shl,   WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_SHR_I4]:    [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_AND_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_OR_I4]: [WasmOpcode.i32_or, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_XOR_I4]: [WasmOpcode.i32_xor, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_SHL_I4]: [WasmOpcode.i32_shl, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_SHR_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
     [MintOpcode.MINT_SHR_UN_I4]: [WasmOpcode.i32_shr_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
 
-    [MintOpcode.MINT_ADD_I8]:    [WasmOpcode.i64_add,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_SUB_I8]:    [WasmOpcode.i64_sub,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_MUL_I8]:    [WasmOpcode.i64_mul,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_DIV_I8]:    [WasmOpcode.i64_div_s, WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_REM_I8]:    [WasmOpcode.i64_rem_s, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_ADD_I8]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_SUB_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_MUL_I8]: [WasmOpcode.i64_mul, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_DIV_I8]: [WasmOpcode.i64_div_s, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_REM_I8]: [WasmOpcode.i64_rem_s, WasmOpcode.i64_load, WasmOpcode.i64_store],
     [MintOpcode.MINT_DIV_UN_I8]: [WasmOpcode.i64_div_u, WasmOpcode.i64_load, WasmOpcode.i64_store],
     [MintOpcode.MINT_REM_UN_I8]: [WasmOpcode.i64_rem_u, WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_AND_I8]:    [WasmOpcode.i64_and,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_OR_I8]:     [WasmOpcode.i64_or,    WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_XOR_I8]:    [WasmOpcode.i64_xor,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_SHL_I8]:    [WasmOpcode.i64_shl,   WasmOpcode.i64_load, WasmOpcode.i64_store],
-    [MintOpcode.MINT_SHR_I8]:    [WasmOpcode.i64_shr_s, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_AND_I8]: [WasmOpcode.i64_and, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_OR_I8]: [WasmOpcode.i64_or, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_XOR_I8]: [WasmOpcode.i64_xor, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_SHL_I8]: [WasmOpcode.i64_shl, WasmOpcode.i64_load, WasmOpcode.i64_store],
+    [MintOpcode.MINT_SHR_I8]: [WasmOpcode.i64_shr_s, WasmOpcode.i64_load, WasmOpcode.i64_store],
     [MintOpcode.MINT_SHR_UN_I8]: [WasmOpcode.i64_shr_u, WasmOpcode.i64_load, WasmOpcode.i64_store],
 
-    [MintOpcode.MINT_ADD_R4]:    [WasmOpcode.f32_add,   WasmOpcode.f32_load, WasmOpcode.f32_store],
-    [MintOpcode.MINT_SUB_R4]:    [WasmOpcode.f32_sub,   WasmOpcode.f32_load, WasmOpcode.f32_store],
-    [MintOpcode.MINT_MUL_R4]:    [WasmOpcode.f32_mul,   WasmOpcode.f32_load, WasmOpcode.f32_store],
-    [MintOpcode.MINT_DIV_R4]:    [WasmOpcode.f32_div,   WasmOpcode.f32_load, WasmOpcode.f32_store],
-
-    [MintOpcode.MINT_ADD_R8]:    [WasmOpcode.f64_add,   WasmOpcode.f64_load, WasmOpcode.f64_store],
-    [MintOpcode.MINT_SUB_R8]:    [WasmOpcode.f64_sub,   WasmOpcode.f64_load, WasmOpcode.f64_store],
-    [MintOpcode.MINT_MUL_R8]:    [WasmOpcode.f64_mul,   WasmOpcode.f64_load, WasmOpcode.f64_store],
-    [MintOpcode.MINT_DIV_R8]:    [WasmOpcode.f64_div,   WasmOpcode.f64_load, WasmOpcode.f64_store],
-
-    [MintOpcode.MINT_CEQ_I4]:    [WasmOpcode.i32_eq,    WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CNE_I4]:    [WasmOpcode.i32_ne,    WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CLT_I4]:    [WasmOpcode.i32_lt_s,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGT_I4]:    [WasmOpcode.i32_gt_s,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CLE_I4]:    [WasmOpcode.i32_le_s,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGE_I4]:    [WasmOpcode.i32_ge_s,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-
-    [MintOpcode.MINT_CLT_UN_I4]: [WasmOpcode.i32_lt_u,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGT_UN_I4]: [WasmOpcode.i32_gt_u,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CLE_UN_I4]: [WasmOpcode.i32_le_u,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGE_UN_I4]: [WasmOpcode.i32_ge_u,  WasmOpcode.i32_load, WasmOpcode.i32_store],
-
-    [MintOpcode.MINT_CEQ_I8]:    [WasmOpcode.i64_eq,    WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CNE_I8]:    [WasmOpcode.i64_ne,    WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CLT_I8]:    [WasmOpcode.i64_lt_s,  WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGT_I8]:    [WasmOpcode.i64_gt_s,  WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CLE_I8]:    [WasmOpcode.i64_le_s,  WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGE_I8]:    [WasmOpcode.i64_ge_s,  WasmOpcode.i64_load, WasmOpcode.i32_store],
-
-    [MintOpcode.MINT_CLT_UN_I8]: [WasmOpcode.i64_lt_u,  WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGT_UN_I8]: [WasmOpcode.i64_gt_u,  WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CLE_UN_I8]: [WasmOpcode.i64_le_u,  WasmOpcode.i64_load, WasmOpcode.i32_store],
-    [MintOpcode.MINT_CGE_UN_I8]: [WasmOpcode.i64_ge_u,  WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_ADD_R4]: [WasmOpcode.f32_add, WasmOpcode.f32_load, WasmOpcode.f32_store],
+    [MintOpcode.MINT_SUB_R4]: [WasmOpcode.f32_sub, WasmOpcode.f32_load, WasmOpcode.f32_store],
+    [MintOpcode.MINT_MUL_R4]: [WasmOpcode.f32_mul, WasmOpcode.f32_load, WasmOpcode.f32_store],
+    [MintOpcode.MINT_DIV_R4]: [WasmOpcode.f32_div, WasmOpcode.f32_load, WasmOpcode.f32_store],
+
+    [MintOpcode.MINT_ADD_R8]: [WasmOpcode.f64_add, WasmOpcode.f64_load, WasmOpcode.f64_store],
+    [MintOpcode.MINT_SUB_R8]: [WasmOpcode.f64_sub, WasmOpcode.f64_load, WasmOpcode.f64_store],
+    [MintOpcode.MINT_MUL_R8]: [WasmOpcode.f64_mul, WasmOpcode.f64_load, WasmOpcode.f64_store],
+    [MintOpcode.MINT_DIV_R8]: [WasmOpcode.f64_div, WasmOpcode.f64_load, WasmOpcode.f64_store],
+
+    [MintOpcode.MINT_CEQ_I4]: [WasmOpcode.i32_eq, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CNE_I4]: [WasmOpcode.i32_ne, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CLT_I4]: [WasmOpcode.i32_lt_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGT_I4]: [WasmOpcode.i32_gt_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CLE_I4]: [WasmOpcode.i32_le_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGE_I4]: [WasmOpcode.i32_ge_s, WasmOpcode.i32_load, WasmOpcode.i32_store],
+
+    [MintOpcode.MINT_CLT_UN_I4]: [WasmOpcode.i32_lt_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGT_UN_I4]: [WasmOpcode.i32_gt_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CLE_UN_I4]: [WasmOpcode.i32_le_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGE_UN_I4]: [WasmOpcode.i32_ge_u, WasmOpcode.i32_load, WasmOpcode.i32_store],
+
+    [MintOpcode.MINT_CEQ_I8]: [WasmOpcode.i64_eq, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CNE_I8]: [WasmOpcode.i64_ne, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CLT_I8]: [WasmOpcode.i64_lt_s, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGT_I8]: [WasmOpcode.i64_gt_s, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CLE_I8]: [WasmOpcode.i64_le_s, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGE_I8]: [WasmOpcode.i64_ge_s, WasmOpcode.i64_load, WasmOpcode.i32_store],
+
+    [MintOpcode.MINT_CLT_UN_I8]: [WasmOpcode.i64_lt_u, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGT_UN_I8]: [WasmOpcode.i64_gt_u, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CLE_UN_I8]: [WasmOpcode.i64_le_u, WasmOpcode.i64_load, WasmOpcode.i32_store],
+    [MintOpcode.MINT_CGE_UN_I8]: [WasmOpcode.i64_ge_u, WasmOpcode.i64_load, WasmOpcode.i32_store],
 
 };
 
-const relopbranchTable : { [opcode: number]: [comparisonOpcode: MintOpcode, immediateOpcode: WasmOpcode | false, isSafepoint: boolean] | MintOpcode | undefined } = {
-    [MintOpcode.MINT_BEQ_I4_S]:         MintOpcode.MINT_CEQ_I4,
-    [MintOpcode.MINT_BNE_UN_I4_S]:      MintOpcode.MINT_CNE_I4,
-    [MintOpcode.MINT_BGT_I4_S]:         MintOpcode.MINT_CGT_I4,
-    [MintOpcode.MINT_BGT_UN_I4_S]:      MintOpcode.MINT_CGT_UN_I4,
-    [MintOpcode.MINT_BLT_I4_S]:         MintOpcode.MINT_CLT_I4,
-    [MintOpcode.MINT_BLT_UN_I4_S]:      MintOpcode.MINT_CLT_UN_I4,
-    [MintOpcode.MINT_BGE_I4_S]:         MintOpcode.MINT_CGE_I4,
-    [MintOpcode.MINT_BGE_UN_I4_S]:      MintOpcode.MINT_CGE_UN_I4,
-    [MintOpcode.MINT_BLE_I4_S]:         MintOpcode.MINT_CLE_I4,
-    [MintOpcode.MINT_BLE_UN_I4_S]:      MintOpcode.MINT_CLE_UN_I4,
-
-    [MintOpcode.MINT_BEQ_I4_SP]:        [MintOpcode.MINT_CEQ_I4, false, true],
-    [MintOpcode.MINT_BNE_UN_I4_SP]:     [MintOpcode.MINT_CNE_I4, false, true],
-    [MintOpcode.MINT_BGT_I4_SP]:        [MintOpcode.MINT_CGT_I4, false, true],
-    [MintOpcode.MINT_BGT_UN_I4_SP]:     [MintOpcode.MINT_CGT_UN_I4, false, true],
-    [MintOpcode.MINT_BLT_I4_SP]:        [MintOpcode.MINT_CLT_I4, false, true],
-    [MintOpcode.MINT_BLT_UN_I4_SP]:     [MintOpcode.MINT_CLT_UN_I4, false, true],
-    [MintOpcode.MINT_BGE_I4_SP]:        [MintOpcode.MINT_CGE_I4, false, true],
-    [MintOpcode.MINT_BGE_UN_I4_SP]:     [MintOpcode.MINT_CGE_UN_I4, false, true],
-    [MintOpcode.MINT_BLE_I4_SP]:        [MintOpcode.MINT_CLE_I4, false, true],
-    [MintOpcode.MINT_BLE_UN_I4_SP]:     [MintOpcode.MINT_CLE_UN_I4, false, true],
-
-    [MintOpcode.MINT_BEQ_I4_IMM_SP]:    [MintOpcode.MINT_CEQ_I4,    WasmOpcode.i32_const, true],
-    [MintOpcode.MINT_BNE_UN_I4_IMM_SP]: [MintOpcode.MINT_CNE_I4,    WasmOpcode.i32_const, true],
-    [MintOpcode.MINT_BGT_I4_IMM_SP]:    [MintOpcode.MINT_CGT_I4,    WasmOpcode.i32_const, true],
+const relopbranchTable: { [opcode: number]: [comparisonOpcode: MintOpcode, immediateOpcode: WasmOpcode | false, isSafepoint: boolean] | MintOpcode | undefined } = {
+    [MintOpcode.MINT_BEQ_I4_S]: MintOpcode.MINT_CEQ_I4,
+    [MintOpcode.MINT_BNE_UN_I4_S]: MintOpcode.MINT_CNE_I4,
+    [MintOpcode.MINT_BGT_I4_S]: MintOpcode.MINT_CGT_I4,
+    [MintOpcode.MINT_BGT_UN_I4_S]: MintOpcode.MINT_CGT_UN_I4,
+    [MintOpcode.MINT_BLT_I4_S]: MintOpcode.MINT_CLT_I4,
+    [MintOpcode.MINT_BLT_UN_I4_S]: MintOpcode.MINT_CLT_UN_I4,
+    [MintOpcode.MINT_BGE_I4_S]: MintOpcode.MINT_CGE_I4,
+    [MintOpcode.MINT_BGE_UN_I4_S]: MintOpcode.MINT_CGE_UN_I4,
+    [MintOpcode.MINT_BLE_I4_S]: MintOpcode.MINT_CLE_I4,
+    [MintOpcode.MINT_BLE_UN_I4_S]: MintOpcode.MINT_CLE_UN_I4,
+
+    [MintOpcode.MINT_BEQ_I4_SP]: [MintOpcode.MINT_CEQ_I4, false, true],
+    [MintOpcode.MINT_BNE_UN_I4_SP]: [MintOpcode.MINT_CNE_I4, false, true],
+    [MintOpcode.MINT_BGT_I4_SP]: [MintOpcode.MINT_CGT_I4, false, true],
+    [MintOpcode.MINT_BGT_UN_I4_SP]: [MintOpcode.MINT_CGT_UN_I4, false, true],
+    [MintOpcode.MINT_BLT_I4_SP]: [MintOpcode.MINT_CLT_I4, false, true],
+    [MintOpcode.MINT_BLT_UN_I4_SP]: [MintOpcode.MINT_CLT_UN_I4, false, true],
+    [MintOpcode.MINT_BGE_I4_SP]: [MintOpcode.MINT_CGE_I4, false, true],
+    [MintOpcode.MINT_BGE_UN_I4_SP]: [MintOpcode.MINT_CGE_UN_I4, false, true],
+    [MintOpcode.MINT_BLE_I4_SP]: [MintOpcode.MINT_CLE_I4, false, true],
+    [MintOpcode.MINT_BLE_UN_I4_SP]: [MintOpcode.MINT_CLE_UN_I4, false, true],
+
+    [MintOpcode.MINT_BEQ_I4_IMM_SP]: [MintOpcode.MINT_CEQ_I4, WasmOpcode.i32_const, true],
+    [MintOpcode.MINT_BNE_UN_I4_IMM_SP]: [MintOpcode.MINT_CNE_I4, WasmOpcode.i32_const, true],
+    [MintOpcode.MINT_BGT_I4_IMM_SP]: [MintOpcode.MINT_CGT_I4, WasmOpcode.i32_const, true],
     [MintOpcode.MINT_BGT_UN_I4_IMM_SP]: [MintOpcode.MINT_CGT_UN_I4, WasmOpcode.i32_const, true],
-    [MintOpcode.MINT_BLT_I4_IMM_SP]:    [MintOpcode.MINT_CLT_I4,    WasmOpcode.i32_const, true],
+    [MintOpcode.MINT_BLT_I4_IMM_SP]: [MintOpcode.MINT_CLT_I4, WasmOpcode.i32_const, true],
     [MintOpcode.MINT_BLT_UN_I4_IMM_SP]: [MintOpcode.MINT_CLT_UN_I4, WasmOpcode.i32_const, true],
-    [MintOpcode.MINT_BGE_I4_IMM_SP]:    [MintOpcode.MINT_CGE_I4,    WasmOpcode.i32_const, true],
+    [MintOpcode.MINT_BGE_I4_IMM_SP]: [MintOpcode.MINT_CGE_I4, WasmOpcode.i32_const, true],
     [MintOpcode.MINT_BGE_UN_I4_IMM_SP]: [MintOpcode.MINT_CGE_UN_I4, WasmOpcode.i32_const, true],
-    [MintOpcode.MINT_BLE_I4_IMM_SP]:    [MintOpcode.MINT_CLE_I4,    WasmOpcode.i32_const, true],
+    [MintOpcode.MINT_BLE_I4_IMM_SP]: [MintOpcode.MINT_CLE_I4, WasmOpcode.i32_const, true],
     [MintOpcode.MINT_BLE_UN_I4_IMM_SP]: [MintOpcode.MINT_CLE_UN_I4, WasmOpcode.i32_const, true],
 
-    [MintOpcode.MINT_BEQ_I8_S]:         MintOpcode.MINT_CEQ_I8,
-    [MintOpcode.MINT_BNE_UN_I8_S]:      MintOpcode.MINT_CNE_I8,
-    [MintOpcode.MINT_BGT_I8_S]:         MintOpcode.MINT_CGT_I8,
-    [MintOpcode.MINT_BGT_UN_I8_S]:      MintOpcode.MINT_CGT_UN_I8,
-    [MintOpcode.MINT_BLT_I8_S]:         MintOpcode.MINT_CLT_I8,
-    [MintOpcode.MINT_BLT_UN_I8_S]:      MintOpcode.MINT_CLT_UN_I8,
-    [MintOpcode.MINT_BGE_I8_S]:         MintOpcode.MINT_CGE_I8,
-    [MintOpcode.MINT_BGE_UN_I8_S]:      MintOpcode.MINT_CGE_UN_I8,
-    [MintOpcode.MINT_BLE_I8_S]:         MintOpcode.MINT_CLE_I8,
-    [MintOpcode.MINT_BLE_UN_I8_S]:      MintOpcode.MINT_CLE_UN_I8,
-
-    [MintOpcode.MINT_BEQ_I8_IMM_SP]:    [MintOpcode.MINT_CEQ_I8,    WasmOpcode.i64_const, true],
+    [MintOpcode.MINT_BEQ_I8_S]: MintOpcode.MINT_CEQ_I8,
+    [MintOpcode.MINT_BNE_UN_I8_S]: MintOpcode.MINT_CNE_I8,
+    [MintOpcode.MINT_BGT_I8_S]: MintOpcode.MINT_CGT_I8,
+    [MintOpcode.MINT_BGT_UN_I8_S]: MintOpcode.MINT_CGT_UN_I8,
+    [MintOpcode.MINT_BLT_I8_S]: MintOpcode.MINT_CLT_I8,
+    [MintOpcode.MINT_BLT_UN_I8_S]: MintOpcode.MINT_CLT_UN_I8,
+    [MintOpcode.MINT_BGE_I8_S]: MintOpcode.MINT_CGE_I8,
+    [MintOpcode.MINT_BGE_UN_I8_S]: MintOpcode.MINT_CGE_UN_I8,
+    [MintOpcode.MINT_BLE_I8_S]: MintOpcode.MINT_CLE_I8,
+    [MintOpcode.MINT_BLE_UN_I8_S]: MintOpcode.MINT_CLE_UN_I8,
+
+    [MintOpcode.MINT_BEQ_I8_IMM_SP]: [MintOpcode.MINT_CEQ_I8, WasmOpcode.i64_const, true],
     // FIXME: Missing compare opcode
     // [MintOpcode.MINT_BNE_UN_I8_IMM_SP]: [MintOpcode.MINT_CNE_UN_I8, WasmOpcode.i64_const, true],
-    [MintOpcode.MINT_BGT_I8_IMM_SP]:    [MintOpcode.MINT_CGT_I8,    WasmOpcode.i64_const, true],
+    [MintOpcode.MINT_BGT_I8_IMM_SP]: [MintOpcode.MINT_CGT_I8, WasmOpcode.i64_const, true],
     [MintOpcode.MINT_BGT_UN_I8_IMM_SP]: [MintOpcode.MINT_CGT_UN_I8, WasmOpcode.i64_const, true],
-    [MintOpcode.MINT_BLT_I8_IMM_SP]:    [MintOpcode.MINT_CLT_I8,    WasmOpcode.i64_const, true],
+    [MintOpcode.MINT_BLT_I8_IMM_SP]: [MintOpcode.MINT_CLT_I8, WasmOpcode.i64_const, true],
     [MintOpcode.MINT_BLT_UN_I8_IMM_SP]: [MintOpcode.MINT_CLT_UN_I8, WasmOpcode.i64_const, true],
-    [MintOpcode.MINT_BGE_I8_IMM_SP]:    [MintOpcode.MINT_CGE_I8,    WasmOpcode.i64_const, true],
+    [MintOpcode.MINT_BGE_I8_IMM_SP]: [MintOpcode.MINT_CGE_I8, WasmOpcode.i64_const, true],
     [MintOpcode.MINT_BGE_UN_I8_IMM_SP]: [MintOpcode.MINT_CGE_UN_I8, WasmOpcode.i64_const, true],
-    [MintOpcode.MINT_BLE_I8_IMM_SP]:    [MintOpcode.MINT_CLE_I8,    WasmOpcode.i64_const, true],
+    [MintOpcode.MINT_BLE_I8_IMM_SP]: [MintOpcode.MINT_CLE_I8, WasmOpcode.i64_const, true],
     [MintOpcode.MINT_BLE_UN_I8_IMM_SP]: [MintOpcode.MINT_CLE_UN_I8, WasmOpcode.i64_const, true],
 
-    [MintOpcode.MINT_BEQ_R4_S]:         MintOpcode.MINT_CEQ_R4,
-    [MintOpcode.MINT_BNE_UN_R4_S]:      <any>JiterpSpecialOpcode.CNE_UN_R4,
-    [MintOpcode.MINT_BGT_R4_S]:         MintOpcode.MINT_CGT_R4,
-    [MintOpcode.MINT_BGT_UN_R4_S]:      MintOpcode.MINT_CGT_UN_R4,
-    [MintOpcode.MINT_BLT_R4_S]:         MintOpcode.MINT_CLT_R4,
-    [MintOpcode.MINT_BLT_UN_R4_S]:      MintOpcode.MINT_CLT_UN_R4,
-    [MintOpcode.MINT_BGE_R4_S]:         MintOpcode.MINT_CGE_R4,
-    [MintOpcode.MINT_BGE_UN_R4_S]:      <any>JiterpSpecialOpcode.CGE_UN_R4,
-    [MintOpcode.MINT_BLE_R4_S]:         MintOpcode.MINT_CLE_R4,
-    [MintOpcode.MINT_BLE_UN_R4_S]:      <any>JiterpSpecialOpcode.CLE_UN_R4,
-
-    [MintOpcode.MINT_BEQ_R8_S]:         MintOpcode.MINT_CEQ_R8,
-    [MintOpcode.MINT_BNE_UN_R8_S]:      <any>JiterpSpecialOpcode.CNE_UN_R8,
-    [MintOpcode.MINT_BGT_R8_S]:         MintOpcode.MINT_CGT_R8,
-    [MintOpcode.MINT_BGT_UN_R8_S]:      MintOpcode.MINT_CGT_UN_R8,
-    [MintOpcode.MINT_BLT_R8_S]:         MintOpcode.MINT_CLT_R8,
-    [MintOpcode.MINT_BLT_UN_R8_S]:      MintOpcode.MINT_CLT_UN_R8,
-    [MintOpcode.MINT_BGE_R8_S]:         MintOpcode.MINT_CGE_R8,
-    [MintOpcode.MINT_BGE_UN_R8_S]:      <any>JiterpSpecialOpcode.CGE_UN_R8,
-    [MintOpcode.MINT_BLE_R8_S]:         MintOpcode.MINT_CLE_R8,
-    [MintOpcode.MINT_BLE_UN_R8_S]:      <any>JiterpSpecialOpcode.CLE_UN_R8,
+    [MintOpcode.MINT_BEQ_R4_S]: MintOpcode.MINT_CEQ_R4,
+    [MintOpcode.MINT_BNE_UN_R4_S]: <any>JiterpSpecialOpcode.CNE_UN_R4,
+    [MintOpcode.MINT_BGT_R4_S]: MintOpcode.MINT_CGT_R4,
+    [MintOpcode.MINT_BGT_UN_R4_S]: MintOpcode.MINT_CGT_UN_R4,
+    [MintOpcode.MINT_BLT_R4_S]: MintOpcode.MINT_CLT_R4,
+    [MintOpcode.MINT_BLT_UN_R4_S]: MintOpcode.MINT_CLT_UN_R4,
+    [MintOpcode.MINT_BGE_R4_S]: MintOpcode.MINT_CGE_R4,
+    [MintOpcode.MINT_BGE_UN_R4_S]: <any>JiterpSpecialOpcode.CGE_UN_R4,
+    [MintOpcode.MINT_BLE_R4_S]: MintOpcode.MINT_CLE_R4,
+    [MintOpcode.MINT_BLE_UN_R4_S]: <any>JiterpSpecialOpcode.CLE_UN_R4,
+
+    [MintOpcode.MINT_BEQ_R8_S]: MintOpcode.MINT_CEQ_R8,
+    [MintOpcode.MINT_BNE_UN_R8_S]: <any>JiterpSpecialOpcode.CNE_UN_R8,
+    [MintOpcode.MINT_BGT_R8_S]: MintOpcode.MINT_CGT_R8,
+    [MintOpcode.MINT_BGT_UN_R8_S]: MintOpcode.MINT_CGT_UN_R8,
+    [MintOpcode.MINT_BLT_R8_S]: MintOpcode.MINT_CLT_R8,
+    [MintOpcode.MINT_BLT_UN_R8_S]: MintOpcode.MINT_CLT_UN_R8,
+    [MintOpcode.MINT_BGE_R8_S]: MintOpcode.MINT_CGE_R8,
+    [MintOpcode.MINT_BGE_UN_R8_S]: <any>JiterpSpecialOpcode.CGE_UN_R8,
+    [MintOpcode.MINT_BLE_R8_S]: MintOpcode.MINT_CLE_R8,
+    [MintOpcode.MINT_BLE_UN_R8_S]: <any>JiterpSpecialOpcode.CLE_UN_R8,
 };
 
-function emit_binop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode) : boolean {
+function emit_binop(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean {
     // operands are popped right to left, which means you build the arg list left to right
-    let lhsLoadOp : WasmOpcode, rhsLoadOp : WasmOpcode, storeOp : WasmOpcode,
+    let lhsLoadOp: WasmOpcode, rhsLoadOp: WasmOpcode, storeOp: WasmOpcode,
         lhsVar = "math_lhs32", rhsVar = "math_rhs32",
-        info : OpRec3 | OpRec4 | undefined,
+        info: OpRec3 | OpRec4 | undefined,
         operandsCached = false;
 
     const intrinsicFpBinop = intrinsicFpBinops[opcode];
@@ -2454,7 +2456,7 @@ function emit_binop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode
     return true;
 }
 
-function emit_unop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode) : boolean {
+function emit_unop(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean {
     // operands are popped right to left, which means you build the arg list left to right
     const info = unopTable[<any>opcode];
     if (!info)
@@ -2577,7 +2579,7 @@ function emit_unop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode)
     return true;
 }
 
-function append_call_handler_store_ret_ip (
+function append_call_handler_store_ret_ip(
     builder: WasmBuilder, ip: MintOpcodePtr,
     frame: NativePointer, opcode: MintOpcode
 ) {
@@ -2597,10 +2599,10 @@ function append_call_handler_store_ret_ip (
     builder.callHandlerReturnAddresses.push(retIp);
 }
 
-function emit_branch (
+function emit_branch(
     builder: WasmBuilder, ip: MintOpcodePtr,
     frame: NativePointer, opcode: MintOpcode, displacement?: number
-) : boolean {
+): boolean {
     const isSafepoint = (opcode >= MintOpcode.MINT_BRFALSE_I4_SP) &&
         (opcode <= MintOpcode.MINT_BLT_UN_I8_IMM_SP);
     eraseInferredState();
@@ -2761,10 +2763,10 @@ function emit_branch (
     return true;
 }
 
-function emit_relop_branch (
+function emit_relop_branch(
     builder: WasmBuilder, ip: MintOpcodePtr,
     frame: NativePointer, opcode: MintOpcode
-) : boolean {
+): boolean {
     const relopBranchInfo = relopbranchTable[opcode];
     if (!relopBranchInfo)
         return false;
@@ -2824,61 +2826,61 @@ function emit_relop_branch (
     return emit_branch(builder, ip, frame, opcode, displacement);
 }
 
-const mathIntrinsicTable : { [opcode: number] : [isUnary: boolean, isF32: boolean, opcodeOrFuncName: WasmOpcode | string] } = {
-    [MintOpcode.MINT_SQRT]:     [true, false,  WasmOpcode.f64_sqrt],
-    [MintOpcode.MINT_SQRTF]:    [true, true,   WasmOpcode.f32_sqrt],
-    [MintOpcode.MINT_CEILING]:  [true, false,  WasmOpcode.f64_ceil],
-    [MintOpcode.MINT_CEILINGF]: [true, true,   WasmOpcode.f32_ceil],
-    [MintOpcode.MINT_FLOOR]:    [true, false,  WasmOpcode.f64_floor],
-    [MintOpcode.MINT_FLOORF]:   [true, true,   WasmOpcode.f32_floor],
-    [MintOpcode.MINT_ABS]:      [true, false,  WasmOpcode.f64_abs],
-    [MintOpcode.MINT_ABSF]:     [true, true,   WasmOpcode.f32_abs],
-
-    [MintOpcode.MINT_ACOS]:     [true, false,  "acos"],
-    [MintOpcode.MINT_ACOSF]:    [true, true,   "acosf"],
-    [MintOpcode.MINT_ACOSH]:    [true, false,  "acosh"],
-    [MintOpcode.MINT_ACOSHF]:   [true, true,   "acoshf"],
-    [MintOpcode.MINT_COS]:      [true, false,  "cos"],
-    [MintOpcode.MINT_COSF]:     [true, true,   "cosf"],
-    [MintOpcode.MINT_ASIN]:     [true, false,  "asin"],
-    [MintOpcode.MINT_ASINF]:    [true, true,   "asinf"],
-    [MintOpcode.MINT_ASINH]:    [true, false,  "asinh"],
-    [MintOpcode.MINT_ASINHF]:   [true, true,   "asinhf"],
-    [MintOpcode.MINT_SIN]:      [true, false,  "sin"],
-    [MintOpcode.MINT_SINF]:     [true, true,   "sinf"],
-    [MintOpcode.MINT_ATAN]:     [true, false,  "atan"],
-    [MintOpcode.MINT_ATANF]:    [true, true,   "atanf"],
-    [MintOpcode.MINT_ATANH]:    [true, false,  "atanh"],
-    [MintOpcode.MINT_ATANHF]:   [true, true,   "atanhf"],
-    [MintOpcode.MINT_TAN]:      [true, false,  "tan"],
-    [MintOpcode.MINT_TANF]:     [true, true,   "tanf"],
-    [MintOpcode.MINT_CBRT]:     [true, false,  "cbrt"],
-    [MintOpcode.MINT_CBRTF]:    [true, true,   "cbrtf"],
-    [MintOpcode.MINT_EXP]:      [true, false,  "exp"],
-    [MintOpcode.MINT_EXPF]:     [true, true,   "expf"],
-    [MintOpcode.MINT_LOG]:      [true, false,  "log"],
-    [MintOpcode.MINT_LOGF]:     [true, true,   "logf"],
-    [MintOpcode.MINT_LOG2]:     [true, false,  "log2"],
-    [MintOpcode.MINT_LOG2F]:    [true, true,   "log2f"],
-    [MintOpcode.MINT_LOG10]:    [true, false,  "log10"],
-    [MintOpcode.MINT_LOG10F]:   [true, true,   "log10f"],
-
-    [MintOpcode.MINT_MIN]:      [false, false,  WasmOpcode.f64_min],
-    [MintOpcode.MINT_MINF]:     [false, true,   WasmOpcode.f32_min],
-    [MintOpcode.MINT_MAX]:      [false, false,  WasmOpcode.f64_max],
-    [MintOpcode.MINT_MAXF]:     [false, true,   WasmOpcode.f32_max],
-
-    [MintOpcode.MINT_ATAN2]:    [false, false, "atan2"],
-    [MintOpcode.MINT_ATAN2F]:   [false, true,  "atan2f"],
-    [MintOpcode.MINT_POW]:      [false, false, "pow"],
-    [MintOpcode.MINT_POWF]:     [false, true,  "powf"],
-    [MintOpcode.MINT_REM_R8]:   [false, false, "fmod"],
-    [MintOpcode.MINT_REM_R4]:   [false, true,  "fmodf"],
+const mathIntrinsicTable: { [opcode: number]: [isUnary: boolean, isF32: boolean, opcodeOrFuncName: WasmOpcode | string] } = {
+    [MintOpcode.MINT_SQRT]: [true, false, WasmOpcode.f64_sqrt],
+    [MintOpcode.MINT_SQRTF]: [true, true, WasmOpcode.f32_sqrt],
+    [MintOpcode.MINT_CEILING]: [true, false, WasmOpcode.f64_ceil],
+    [MintOpcode.MINT_CEILINGF]: [true, true, WasmOpcode.f32_ceil],
+    [MintOpcode.MINT_FLOOR]: [true, false, WasmOpcode.f64_floor],
+    [MintOpcode.MINT_FLOORF]: [true, true, WasmOpcode.f32_floor],
+    [MintOpcode.MINT_ABS]: [true, false, WasmOpcode.f64_abs],
+    [MintOpcode.MINT_ABSF]: [true, true, WasmOpcode.f32_abs],
+
+    [MintOpcode.MINT_ACOS]: [true, false, "acos"],
+    [MintOpcode.MINT_ACOSF]: [true, true, "acosf"],
+    [MintOpcode.MINT_ACOSH]: [true, false, "acosh"],
+    [MintOpcode.MINT_ACOSHF]: [true, true, "acoshf"],
+    [MintOpcode.MINT_COS]: [true, false, "cos"],
+    [MintOpcode.MINT_COSF]: [true, true, "cosf"],
+    [MintOpcode.MINT_ASIN]: [true, false, "asin"],
+    [MintOpcode.MINT_ASINF]: [true, true, "asinf"],
+    [MintOpcode.MINT_ASINH]: [true, false, "asinh"],
+    [MintOpcode.MINT_ASINHF]: [true, true, "asinhf"],
+    [MintOpcode.MINT_SIN]: [true, false, "sin"],
+    [MintOpcode.MINT_SINF]: [true, true, "sinf"],
+    [MintOpcode.MINT_ATAN]: [true, false, "atan"],
+    [MintOpcode.MINT_ATANF]: [true, true, "atanf"],
+    [MintOpcode.MINT_ATANH]: [true, false, "atanh"],
+    [MintOpcode.MINT_ATANHF]: [true, true, "atanhf"],
+    [MintOpcode.MINT_TAN]: [true, false, "tan"],
+    [MintOpcode.MINT_TANF]: [true, true, "tanf"],
+    [MintOpcode.MINT_CBRT]: [true, false, "cbrt"],
+    [MintOpcode.MINT_CBRTF]: [true, true, "cbrtf"],
+    [MintOpcode.MINT_EXP]: [true, false, "exp"],
+    [MintOpcode.MINT_EXPF]: [true, true, "expf"],
+    [MintOpcode.MINT_LOG]: [true, false, "log"],
+    [MintOpcode.MINT_LOGF]: [true, true, "logf"],
+    [MintOpcode.MINT_LOG2]: [true, false, "log2"],
+    [MintOpcode.MINT_LOG2F]: [true, true, "log2f"],
+    [MintOpcode.MINT_LOG10]: [true, false, "log10"],
+    [MintOpcode.MINT_LOG10F]: [true, true, "log10f"],
+
+    [MintOpcode.MINT_MIN]: [false, false, WasmOpcode.f64_min],
+    [MintOpcode.MINT_MINF]: [false, true, WasmOpcode.f32_min],
+    [MintOpcode.MINT_MAX]: [false, false, WasmOpcode.f64_max],
+    [MintOpcode.MINT_MAXF]: [false, true, WasmOpcode.f32_max],
+
+    [MintOpcode.MINT_ATAN2]: [false, false, "atan2"],
+    [MintOpcode.MINT_ATAN2F]: [false, true, "atan2f"],
+    [MintOpcode.MINT_POW]: [false, false, "pow"],
+    [MintOpcode.MINT_POWF]: [false, true, "powf"],
+    [MintOpcode.MINT_REM_R8]: [false, false, "fmod"],
+    [MintOpcode.MINT_REM_R4]: [false, true, "fmodf"],
 };
 
-function emit_math_intrinsic (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode) : boolean {
-    let isUnary : boolean, isF32 : boolean, name: string | undefined;
-    let wasmOp : WasmOpcode | undefined;
+function emit_math_intrinsic(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean {
+    let isUnary: boolean, isF32: boolean, name: string | undefined;
+    let wasmOp: WasmOpcode | undefined;
     const destOffset = getArgU16(ip, 1),
         srcOffset = getArgU16(ip, 2),
         rhsOffset = getArgU16(ip, 3);
@@ -2924,7 +2926,7 @@ function emit_math_intrinsic (builder: WasmBuilder, ip: MintOpcodePtr, opcode: M
     }
 }
 
-function emit_indirectop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode) : boolean {
+function emit_indirectop(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean {
     const isLoad = (opcode >= MintOpcode.MINT_LDIND_I1) &&
         (opcode <= MintOpcode.MINT_LDIND_OFFSET_ADD_MUL_IMM_I8);
     const isAddMul = (
@@ -2934,17 +2936,19 @@ function emit_indirectop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintO
     const isOffset = (
         (opcode >= MintOpcode.MINT_LDIND_OFFSET_I1) &&
         (opcode <= MintOpcode.MINT_LDIND_OFFSET_IMM_I8)
-    ) || (
-        (opcode >= MintOpcode.MINT_STIND_OFFSET_I1) &&
-        (opcode <= MintOpcode.MINT_STIND_OFFSET_IMM_I8)
-    ) || isAddMul;
+    ) ||
+        (
+            (opcode >= MintOpcode.MINT_STIND_OFFSET_I1) &&
+            (opcode <= MintOpcode.MINT_STIND_OFFSET_IMM_I8)
+        ) || isAddMul;
     const isImm = (
         (opcode >= MintOpcode.MINT_LDIND_OFFSET_IMM_I1) &&
         (opcode <= MintOpcode.MINT_LDIND_OFFSET_IMM_I8)
-    ) || (
-        (opcode >= MintOpcode.MINT_STIND_OFFSET_IMM_I1) &&
-        (opcode <= MintOpcode.MINT_STIND_OFFSET_IMM_I8)
-    ) || isAddMul;
+    ) ||
+        (
+            (opcode >= MintOpcode.MINT_STIND_OFFSET_IMM_I1) &&
+            (opcode <= MintOpcode.MINT_STIND_OFFSET_IMM_I8)
+        ) || isAddMul;
 
     let valueVarIndex, addressVarIndex, offsetVarIndex = -1, constantOffset = 0,
         constantMultiplier = 1;
@@ -2984,7 +2988,7 @@ function emit_indirectop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintO
         valueVarIndex = getArgU16(ip, 2);
     }
 
-    let getter : WasmOpcode, setter = WasmOpcode.i32_store;
+    let getter: WasmOpcode, setter = WasmOpcode.i32_store;
     switch (opcode) {
         case MintOpcode.MINT_LDIND_I1:
         case MintOpcode.MINT_LDIND_OFFSET_I1:
@@ -3120,7 +3124,7 @@ function emit_indirectop (builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintO
     return true;
 }
 
-function append_getelema1 (
+function append_getelema1(
     builder: WasmBuilder, ip: MintOpcodePtr,
     objectOffset: number, indexOffset: number, elementSize: number
 ) {
@@ -3157,11 +3161,9 @@ function append_getelema1 (
     // append_getelema1 leaves the address on the stack
 }
 
-function emit_arrayop (builder: WasmBuilder, frame: NativePointer, ip: MintOpcodePtr, opcode: MintOpcode) : boolean {
-    const isLoad = (
-            (opcode <= MintOpcode.MINT_LDELEMA_TC) &&
-            (opcode >= MintOpcode.MINT_LDELEM_I1)
-        ) || (opcode === MintOpcode.MINT_LDLEN),
+function emit_arrayop(builder: WasmBuilder, frame: NativePointer, ip: MintOpcodePtr, opcode: MintOpcode): boolean {
+    const isLoad = ((opcode <= MintOpcode.MINT_LDELEMA_TC) && (opcode >= MintOpcode.MINT_LDELEM_I1)) ||
+        (opcode === MintOpcode.MINT_LDLEN),
         objectOffset = getArgU16(ip, isLoad ? 2 : 1),
         valueOffset = getArgU16(ip, isLoad ? 1 : 3),
         indexOffset = getArgU16(ip, isLoad ? 3 : 2);
@@ -3313,9 +3315,9 @@ function emit_arrayop (builder: WasmBuilder, frame: NativePointer, ip: MintOpcod
 
 const vec128Test =
     "0061736d0100000001040160000003020100070801047465737400000a090107004100fd111a0b";
-let wasmSimdSupported : boolean | undefined;
+let wasmSimdSupported: boolean | undefined;
 
-function getIsWasmSimdSupported () : boolean {
+function getIsWasmSimdSupported(): boolean {
     if (wasmSimdSupported !== undefined)
         return wasmSimdSupported;
 
@@ -3334,10 +3336,10 @@ function getIsWasmSimdSupported () : boolean {
     return wasmSimdSupported;
 }
 
-function get_import_name (
+function get_import_name(
     builder: WasmBuilder, typeName: string,
     functionPtr: number
-) : string {
+): string {
     const name = `${typeName}_${functionPtr.toString(16)}`;
     if (typeof (builder.importedFunctions[name]) !== "object")
         builder.defineImportedFunction("s", name, typeName, false, functionPtr);
@@ -3366,11 +3368,11 @@ const simdCreateStoreOps = {
     [MintOpcode.MINT_SIMD_V128_I8_CREATE]: WasmOpcode.i64_store,
 };
 
-function emit_simd (
+function emit_simd(
     builder: WasmBuilder, ip: MintOpcodePtr,
     opcode: MintOpcode, opname: string,
     argCount: number, index: number
-) : boolean {
+): boolean {
     // First, if compiling an intrinsic attempt to emit the special vectorized implementation
     // We only do this if SIMD is enabled since we'll be using the v128 opcodes.
     if (builder.options.enableSimd && getIsWasmSimdSupported()) {
@@ -3467,24 +3469,24 @@ function emit_simd (
     }
 }
 
-function append_simd_store (builder: WasmBuilder, ip: MintOpcodePtr) {
+function append_simd_store(builder: WasmBuilder, ip: MintOpcodePtr) {
     append_stloc_tail(builder, getArgU16(ip, 1), WasmOpcode.PREFIX_simd, WasmSimdOpcode.v128_store);
 }
 
-function append_simd_2_load (builder: WasmBuilder, ip: MintOpcodePtr, loadOp?: WasmSimdOpcode) {
+function append_simd_2_load(builder: WasmBuilder, ip: MintOpcodePtr, loadOp?: WasmSimdOpcode) {
     builder.local("pLocals");
     // This || is harmless since v128_load is 0
     append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.PREFIX_simd, loadOp || WasmSimdOpcode.v128_load);
 }
 
-function append_simd_3_load (builder: WasmBuilder, ip: MintOpcodePtr) {
+function append_simd_3_load(builder: WasmBuilder, ip: MintOpcodePtr) {
     builder.local("pLocals");
     append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.PREFIX_simd, WasmSimdOpcode.v128_load);
     // FIXME: Can rhs be a scalar? We handle shifts separately already
     append_ldloc(builder, getArgU16(ip, 3), WasmOpcode.PREFIX_simd, WasmSimdOpcode.v128_load);
 }
 
-function append_simd_4_load (builder: WasmBuilder, ip: MintOpcodePtr) {
+function append_simd_4_load(builder: WasmBuilder, ip: MintOpcodePtr) {
     builder.local("pLocals");
     append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.PREFIX_simd, WasmSimdOpcode.v128_load);
     append_ldloc(builder, getArgU16(ip, 3), WasmOpcode.PREFIX_simd, WasmSimdOpcode.v128_load);
@@ -3507,14 +3509,14 @@ const simdShiftTable = new Set<SimdIntrinsic3>([
     SimdIntrinsic3.V128_I8_URIGHT_SHIFT,
 ]);
 
-function append_stloc_simd_zero (builder: WasmBuilder, offset: number) {
+function append_stloc_simd_zero(builder: WasmBuilder, offset: number) {
     builder.local("pLocals");
     builder.appendSimd(WasmSimdOpcode.v128_const);
     builder.appendBytes(new Uint8Array(sizeOfV128));
     append_stloc_tail(builder, offset, WasmOpcode.PREFIX_simd, WasmSimdOpcode.v128_store);
 }
 
-function emit_simd_2 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrinsic2) : boolean {
+function emit_simd_2(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrinsic2): boolean {
     const simple = <WasmSimdOpcode>cwraps.mono_jiterp_get_simd_opcode(1, index);
     if (simple) {
         append_simd_2_load(builder, ip);
@@ -3575,7 +3577,7 @@ function emit_simd_2 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrin
     }
 }
 
-function emit_simd_3 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrinsic3) : boolean {
+function emit_simd_3(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrinsic3): boolean {
     const simple = <WasmSimdOpcode>cwraps.mono_jiterp_get_simd_opcode(2, index);
     if (simple) {
         const isShift = simdShiftTable.has(index);
@@ -3611,7 +3613,7 @@ function emit_simd_3 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrin
     return false;
 }
 
-function emit_simd_4 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrinsic4) : boolean {
+function emit_simd_4(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrinsic4): boolean {
     const simple = <WasmSimdOpcode>cwraps.mono_jiterp_get_simd_opcode(3, index);
     if (simple) {
         append_simd_4_load(builder, ip);
@@ -3636,7 +3638,7 @@ function emit_simd_4 (builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrin
     }
 }
 
-function append_safepoint (builder: WasmBuilder, ip: MintOpcodePtr) {
+function append_safepoint(builder: WasmBuilder, ip: MintOpcodePtr) {
     // Check whether a safepoint is required
     builder.ptr_const(cwraps.mono_jiterp_get_polling_required_address());
     builder.appendU8(WasmOpcode.i32_load);
index 2bb9354..81fe238 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { mono_assert, MonoMethod } from "./types";
+import { MonoMethod } from "./types/internal";
 import { NativePointer } from "./types/emscripten";
 import { Module, runtimeHelpers } from "./globals";
 import {
@@ -329,7 +329,7 @@ function wrap_trace_function(
 
     if (!_wrap_trace_function) {
         // If we used a regular closure, the js console would print the entirety of
-        //  dotnet.js when printing an error stack trace, which is... not helpful
+        //  dotnet.native.js when printing an error stack trace, which is... not helpful
         const js = `return function trace_enter (locals) {
             let threw = true;
             try {
diff --git a/src/mono/wasm/runtime/loader/assets.ts b/src/mono/wasm/runtime/loader/assets.ts
new file mode 100644 (file)
index 0000000..a99f80a
--- /dev/null
@@ -0,0 +1,419 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { AssetEntryInternal, PromiseAndController } from "../types/internal";
+import type { AssetBehaviours, AssetEntry, LoadingResource, ResourceRequest } from "../types";
+import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, runtimeHelpers } from "./globals";
+import { createPromiseController } from "./promise-controller";
+
+
+let throttlingPromise: PromiseAndController<void> | undefined;
+// in order to prevent net::ERR_INSUFFICIENT_RESOURCES if we start downloading too many files at same time
+let parallel_count = 0;
+
+// don't `fetch` javaScript files
+const skipDownloadsByAssetTypes: {
+    [k: string]: boolean
+} = {
+    "js-module-threads": true,
+    "js-module-runtime": true,
+    "js-module-native": true,
+    "js-module-dotnet": true,
+    "dotnetwasm": true,
+};
+
+// `response.arrayBuffer()` can't be called twice. Some usecases are calling it on response in the instantiation.
+const skipBufferByAssetTypes: {
+    [k: string]: boolean
+} = {
+    "dotnetwasm": true,
+    "symbols": true,
+};
+
+const containedInSnapshotByAssetTypes: {
+    [k: string]: boolean
+} = {
+    "resource": true,
+    "assembly": true,
+    "pdb": true,
+    "heap": true,
+    "icu": true,
+    "js-module-threads": true,
+    "js-module-runtime": true,
+    "js-module-native": true,
+    "js-module-dotnet": true,
+    "dotnetwasm": true,
+};
+
+// these assets are instantiated differently than the main flow
+const skipInstantiateByAssetTypes: {
+    [k: string]: boolean
+} = {
+    "js-module-threads": true,
+    "js-module-runtime": true,
+    "js-module-native": true,
+    "js-module-dotnet": true,
+    "dotnetwasm": true,
+    "symbols": true,
+};
+
+export function shouldLoadIcuAsset(asset: AssetEntryInternal): boolean {
+    return !(asset.behavior == "icu" && asset.name != loaderHelpers.preferredIcuAsset);
+}
+
+export function resolve_asset_path(behavior: AssetBehaviours): AssetEntryInternal {
+    const asset: AssetEntryInternal | undefined = loaderHelpers.config.assets?.find(a => a.behavior == behavior);
+    mono_assert(asset, () => `Can't find asset for ${behavior}`);
+    if (!asset.resolvedUrl) {
+        asset.resolvedUrl = resolve_path(asset, "");
+    }
+    return asset;
+}
+export async function mono_download_assets(): Promise<void> {
+    if (loaderHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_download_assets");
+    loaderHelpers.maxParallelDownloads = loaderHelpers.config.maxParallelDownloads || loaderHelpers.maxParallelDownloads;
+    loaderHelpers.enableDownloadRetry = loaderHelpers.config.enableDownloadRetry || loaderHelpers.enableDownloadRetry;
+    try {
+        const alwaysLoadedAssets: AssetEntryInternal[] = [];
+        const containedInSnapshotAssets: AssetEntryInternal[] = [];
+        const promises_of_assets: Promise<AssetEntryInternal>[] = [];
+
+        for (const a of loaderHelpers.config.assets!) {
+            const asset: AssetEntryInternal = a;
+            mono_assert(typeof asset === "object", "asset must be object");
+            mono_assert(typeof asset.behavior === "string", "asset behavior must be known string");
+            mono_assert(typeof asset.name === "string", "asset name must be string");
+            mono_assert(!asset.resolvedUrl || typeof asset.resolvedUrl === "string", "asset resolvedUrl could be string");
+            mono_assert(!asset.hash || typeof asset.hash === "string", "asset resolvedUrl could be string");
+            mono_assert(!asset.pendingDownload || typeof asset.pendingDownload === "object", "asset pendingDownload could be object");
+            if (containedInSnapshotByAssetTypes[asset.behavior]) {
+                containedInSnapshotAssets.push(asset);
+            } else {
+                alwaysLoadedAssets.push(asset);
+            }
+        }
+
+        const countAndStartDownload = (asset: AssetEntryInternal) => {
+            if (!skipInstantiateByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
+                loaderHelpers.expected_instantiated_assets_count++;
+            }
+            if (!skipDownloadsByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
+                loaderHelpers.expected_downloaded_assets_count++;
+                promises_of_assets.push(start_asset_download(asset));
+            }
+        };
+
+        // start fetching assets in parallel, only assets which are not part of memory snapshot
+        for (const asset of alwaysLoadedAssets) {
+            countAndStartDownload(asset);
+        }
+
+        // continue after the dotnet.runtime.js was loaded
+        await loaderHelpers.runtimeModuleLoaded.promise;
+
+        // continue after we know if memory snapshot is available or not
+        await runtimeHelpers.memorySnapshotSkippedOrDone.promise;
+
+        // start fetching assets in parallel, only if memory snapshot is not available.
+        for (const asset of containedInSnapshotAssets) {
+            if (!runtimeHelpers.loadedMemorySnapshot) {
+                countAndStartDownload(asset);
+            } else {
+                // Otherwise cleanup in case we were given pending download. It would be even better if we could abort the download.
+                cleanupAsset(asset);
+                // tell the debugger it is loaded
+                if (asset.behavior == "resource" || asset.behavior == "assembly" || asset.behavior == "pdb") {
+                    const url = resolve_path(asset, "");
+                    const virtualName: string = typeof (asset.virtualPath) === "string"
+                        ? asset.virtualPath
+                        : asset.name;
+                    loaderHelpers._loaded_files.push({ url: url, file: virtualName });
+                }
+            }
+        }
+
+        loaderHelpers.allDownloadsQueued.promise_control.resolve();
+        await loaderHelpers.runtimeModuleLoaded.promise;
+
+        const promises_of_asset_instantiation: Promise<void>[] = [];
+        for (const downloadPromise of promises_of_assets) {
+            promises_of_asset_instantiation.push((async () => {
+                const asset = await downloadPromise;
+                if (asset.buffer) {
+                    if (!skipInstantiateByAssetTypes[asset.behavior]) {
+                        const url = asset.pendingDownloadInternal!.url;
+                        mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array or buffer like");
+                        const data = new Uint8Array(asset.buffer!);
+                        cleanupAsset(asset);
+
+                        // wait till after onRuntimeInitialized and after memory snapshot is loaded or skipped
+
+                        await runtimeHelpers.beforeOnRuntimeInitialized.promise;
+                        await runtimeHelpers.memorySnapshotSkippedOrDone.promise;
+                        runtimeHelpers.instantiate_asset(asset, url, data);
+                    }
+                    if (asset.behavior === "symbols") {
+                        await runtimeHelpers.instantiate_symbols_asset(asset);
+                        cleanupAsset(asset);
+                    }
+                } else {
+                    const headersOnly = skipBufferByAssetTypes[asset.behavior];
+                    if (!headersOnly) {
+                        mono_assert(asset.isOptional, "Expected asset to have the downloaded buffer");
+                        if (!skipDownloadsByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
+                            loaderHelpers.expected_downloaded_assets_count--;
+                        }
+                        if (!skipInstantiateByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
+                            loaderHelpers.expected_instantiated_assets_count--;
+                        }
+                    } else {
+                        if (skipBufferByAssetTypes[asset.behavior]) {
+                            ++loaderHelpers.actual_downloaded_assets_count;
+                        }
+                    }
+                }
+            })());
+        }
+
+        // this await will get past the onRuntimeInitialized because we are not blocking via addRunDependency
+        // and we are not awating it here
+        Promise.all(promises_of_asset_instantiation).then(() => {
+            runtimeHelpers.allAssetsInMemory.promise_control.resolve();
+        }).catch(e => {
+            loaderHelpers.err("MONO_WASM: Error in mono_download_assets: " + e);
+            loaderHelpers.abort_startup(e, true);
+        });
+        // OPTIMIZATION explained:
+        // we do it this way so that we could allocate memory immediately after asset is downloaded (and after onRuntimeInitialized which happened already)
+        // spreading in time
+        // rather than to block all downloads after onRuntimeInitialized or block onRuntimeInitialized after all downloads are done. That would create allocation burst.
+    } catch (e: any) {
+        loaderHelpers.err("MONO_WASM: Error in mono_download_assets: " + e);
+        throw e;
+    }
+}
+
+export function delay(ms: number): Promise<void> {
+    return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+// FIXME: Connection reset is probably the only good one for which we should retry
+export async function start_asset_download(asset: AssetEntryInternal): Promise<AssetEntryInternal> {
+    try {
+        return await start_asset_download_with_throttle(asset);
+    } catch (err: any) {
+        if (!loaderHelpers.enableDownloadRetry) {
+            // we will not re-try if disabled
+            throw err;
+        }
+        if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) {
+            // we will not re-try on shell
+            throw err;
+        }
+        if (asset.pendingDownload && asset.pendingDownloadInternal == asset.pendingDownload) {
+            // we will not re-try with external source
+            throw err;
+        }
+        if (asset.resolvedUrl && asset.resolvedUrl.indexOf("file://") != -1) {
+            // we will not re-try with local file
+            throw err;
+        }
+        if (err && err.status == 404) {
+            // we will not re-try with 404
+            throw err;
+        }
+        asset.pendingDownloadInternal = undefined;
+        // second attempt only after all first attempts are queued
+        await loaderHelpers.allDownloadsQueued.promise;
+        try {
+            return await start_asset_download_with_throttle(asset);
+        } catch (err) {
+            asset.pendingDownloadInternal = undefined;
+            // third attempt after small delay
+            await delay(100);
+            return await start_asset_download_with_throttle(asset);
+        }
+    }
+}
+
+async function start_asset_download_with_throttle(asset: AssetEntryInternal): Promise<AssetEntryInternal> {
+    // we don't addRunDependency to allow download in parallel with onRuntimeInitialized event!
+    while (throttlingPromise) {
+        await throttlingPromise.promise;
+    }
+    try {
+        ++parallel_count;
+        if (parallel_count == loaderHelpers.maxParallelDownloads) {
+            if (loaderHelpers.diagnosticTracing)
+                console.debug("MONO_WASM: Throttling further parallel downloads");
+            throttlingPromise = createPromiseController<void>();
+        }
+
+        const response = await start_asset_download_sources(asset);
+        if (!response) {
+            return asset;
+        }
+        const skipBuffer = skipBufferByAssetTypes[asset.behavior];
+        if (skipBuffer) {
+            return asset;
+        }
+        asset.buffer = await response.arrayBuffer();
+        ++loaderHelpers.actual_downloaded_assets_count;
+        return asset;
+    }
+    finally {
+        --parallel_count;
+        if (throttlingPromise && parallel_count == loaderHelpers.maxParallelDownloads - 1) {
+            if (loaderHelpers.diagnosticTracing)
+                console.debug("MONO_WASM: Resuming more parallel downloads");
+            const old_throttling = throttlingPromise;
+            throttlingPromise = undefined;
+            old_throttling.promise_control.resolve();
+        }
+    }
+}
+
+async function start_asset_download_sources(asset: AssetEntryInternal): Promise<Response | undefined> {
+    // we don't addRunDependency to allow download in parallel with onRuntimeInitialized event!
+    if (asset.pendingDownload) {
+        asset.pendingDownloadInternal = asset.pendingDownload;
+    }
+    if (asset.pendingDownloadInternal && asset.pendingDownloadInternal.response) {
+        return asset.pendingDownloadInternal.response;
+    }
+    if (asset.buffer) {
+        const buffer = asset.buffer;
+        asset.buffer = null as any; // GC
+        asset.pendingDownloadInternal = {
+            url: "undefined://" + asset.name,
+            name: asset.name,
+            response: Promise.resolve({
+                arrayBuffer: () => buffer,
+                headers: {
+                    get: () => undefined,
+                }
+            }) as any
+        };
+        return asset.pendingDownloadInternal.response;
+    }
+
+    const sourcesList = asset.loadRemote && loaderHelpers.config.remoteSources ? loaderHelpers.config.remoteSources : [""];
+    let response: Response | undefined = undefined;
+    for (let sourcePrefix of sourcesList) {
+        sourcePrefix = sourcePrefix.trim();
+        // HACK: Special-case because MSBuild doesn't allow "" as an attribute
+        if (sourcePrefix === "./")
+            sourcePrefix = "";
+
+        const attemptUrl = resolve_path(asset, sourcePrefix);
+        if (asset.name === attemptUrl) {
+            if (loaderHelpers.diagnosticTracing)
+                console.debug(`MONO_WASM: Attempting to download '${attemptUrl}'`);
+        } else {
+            if (loaderHelpers.diagnosticTracing)
+                console.debug(`MONO_WASM: Attempting to download '${attemptUrl}' for ${asset.name}`);
+        }
+        try {
+            asset.resolvedUrl = attemptUrl;
+            const loadingResource = download_resource(asset);
+            asset.pendingDownloadInternal = loadingResource;
+            response = await loadingResource.response;
+            if (!response || !response.ok) {
+                continue;// next source
+            }
+            return response;
+        }
+        catch (err) {
+            if (!response) {
+                response = {
+                    ok: false,
+                    url: attemptUrl,
+                    status: 0,
+                    statusText: "" + err,
+                } as any;
+            }
+            continue; //next source
+        }
+    }
+    const isOkToFail = asset.isOptional || (asset.name.match(/\.pdb$/) && loaderHelpers.config.ignorePdbLoadErrors);
+    mono_assert(response, () => `Response undefined ${asset.name}`);
+    if (!isOkToFail) {
+        const err: any = new Error(`MONO_WASM: download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`);
+        err.status = response.status;
+        throw err;
+    } else {
+        loaderHelpers.out(`MONO_WASM: optional download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`);
+        return undefined;
+    }
+}
+
+function resolve_path(asset: AssetEntry, sourcePrefix: string): string {
+    mono_assert(sourcePrefix !== null && sourcePrefix !== undefined, () => `sourcePrefix must be provided for ${asset.name}`);
+    let attemptUrl;
+    const assemblyRootFolder = loaderHelpers.config.assemblyRootFolder;
+    if (!asset.resolvedUrl) {
+        if (sourcePrefix === "") {
+            if (asset.behavior === "assembly" || asset.behavior === "pdb") {
+                attemptUrl = assemblyRootFolder
+                    ? (assemblyRootFolder + "/" + asset.name)
+                    : asset.name;
+            }
+            else if (asset.behavior === "resource") {
+                const path = asset.culture && asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name;
+                attemptUrl = assemblyRootFolder
+                    ? (assemblyRootFolder + "/" + path)
+                    : path;
+            }
+            else {
+                attemptUrl = asset.name;
+            }
+        } else {
+            attemptUrl = sourcePrefix + asset.name;
+        }
+        attemptUrl = loaderHelpers.locateFile(attemptUrl);
+        if (loaderHelpers.assetUniqueQuery) {
+            attemptUrl = attemptUrl + loaderHelpers.assetUniqueQuery;
+        }
+    }
+    else {
+        attemptUrl = asset.resolvedUrl;
+    }
+    mono_assert(attemptUrl && typeof attemptUrl == "string", "attemptUrl need to be path or url string");
+    return attemptUrl;
+}
+
+function download_resource(request: ResourceRequest): LoadingResource {
+    try {
+        if (typeof loaderHelpers.downloadResource === "function") {
+            const loading = loaderHelpers.downloadResource(request);
+            if (loading) return loading;
+        }
+        const options: any = {};
+        if (request.hash) {
+            options.integrity = request.hash;
+        }
+        const response = loaderHelpers.fetch_like(request.resolvedUrl!, options);
+        return {
+            name: request.name, url: request.resolvedUrl!, response
+        };
+    } catch (err) {
+        const response = <Response><any>{
+            ok: false,
+            url: request.resolvedUrl,
+            status: 500,
+            statusText: "ERR29: " + err,
+            arrayBuffer: () => { throw err; },
+            json: () => { throw err; }
+        };
+        return {
+            name: request.name, url: request.resolvedUrl!, response: Promise.resolve(response)
+        };
+    }
+}
+
+export function cleanupAsset(asset: AssetEntryInternal) {
+    // give GC chance to collect resources
+    asset.pendingDownloadInternal = null as any; // GC
+    asset.pendingDownload = null as any; // GC
+    asset.buffer = null as any; // GC
+}
\ No newline at end of file
@@ -1,8 +1,9 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { Module } from "../globals";
-import { WebAssemblyBootResourceType } from "../types-api";
+import type { BootJsonData } from "../../types/blazor";
+import type { WebAssemblyBootResourceType } from "../../types";
+import { loaderHelpers } from "../globals";
 
 type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) => string | Promise<Response> | null | undefined;
 
@@ -25,7 +26,7 @@ export class BootConfigResult {
             bootConfigResponse = await loaderResponse;
         }
 
-        const applicationEnvironment = environment || (Module.getApplicationEnvironment && Module.getApplicationEnvironment(bootConfigResponse)) || "Production";
+        const applicationEnvironment = environment || (loaderHelpers.getApplicationEnvironment && loaderHelpers.getApplicationEnvironment(bootConfigResponse)) || "Production";
         const bootConfig: BootJsonData = await bootConfigResponse.json();
         bootConfig.modifiableAssemblies = bootConfigResponse.headers.get("DOTNET-MODIFIABLE-ASSEMBLIES");
         bootConfig.aspnetCoreBrowserTools = bootConfigResponse.headers.get("ASPNETCORE-BROWSER-TOOLS");
@@ -42,48 +43,3 @@ export class BootConfigResult {
     }
 }
 
-// Keep in sync with Microsoft.NET.Sdk.WebAssembly.BootJsonData from the WasmSDK
-export interface BootJsonData {
-    readonly entryAssembly: string;
-    readonly resources: ResourceGroups;
-    /** Gets a value that determines if this boot config was produced from a non-published build (i.e. dotnet build or dotnet run) */
-    readonly debugBuild: boolean;
-    readonly linkerEnabled: boolean;
-    readonly cacheBootResources: boolean;
-    readonly config: string[];
-    readonly icuDataMode: ICUDataMode;
-    readonly startupMemoryCache: boolean | undefined;
-    readonly runtimeOptions: string[] | undefined;
-
-    // These properties are tacked on, and not found in the boot.json file
-    modifiableAssemblies: string | null;
-    aspnetCoreBrowserTools: string | null;
-}
-
-export type BootJsonDataExtension = { [extensionName: string]: ResourceList };
-
-export interface ResourceGroups {
-    readonly assembly: ResourceList;
-    readonly lazyAssembly: ResourceList;
-    readonly pdb?: ResourceList;
-    readonly runtime: ResourceList;
-    readonly satelliteResources?: { [cultureName: string]: ResourceList };
-    readonly libraryInitializers?: ResourceList,
-    readonly extensions?: BootJsonDataExtension
-    readonly runtimeAssets: ExtendedResourceList;
-}
-
-export type ResourceList = { [name: string]: string };
-export type ExtendedResourceList = {
-    [name: string]: {
-        hash: string,
-        behavior: string
-    }
-};
-
-export enum ICUDataMode {
-    Sharded,
-    All,
-    Invariant,
-    Custom
-}
@@ -1,9 +1,9 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { WebAssemblyBootResourceType, WebAssemblyStartOptions } from "../types-api";
+import type { WebAssemblyBootResourceType, WebAssemblyStartOptions } from "../../types";
+import type { BootJsonData, ResourceList } from "../../types/blazor";
 import { toAbsoluteUri } from "./_Polyfill";
-import { BootJsonData, ResourceList } from "./BootConfig";
 const networkFetchCacheMode = "no-cache";
 
 export class WebAssemblyResourceLoader {
@@ -1,29 +1,79 @@
-import { INTERNAL, Module } from "../globals";
-import { MonoConfigInternal } from "../types";
-import { AssetEntry, LoadingResource, WebAssemblyBootResourceType } from "../types-api";
-import { BootConfigResult, BootJsonData, ICUDataMode } from "./BootConfig";
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { DotnetModuleInternal, MonoConfigInternal } from "../../types/internal";
+import type { AssetBehaviours, AssetEntry, LoadingResource, WebAssemblyBootResourceType } from "../../types";
+import type { BootJsonData } from "../../types/blazor";
+
+import { INTERNAL, loaderHelpers } from "../globals";
+import { BootConfigResult } from "./BootConfig";
 import { WebAssemblyResourceLoader } from "./WebAssemblyResourceLoader";
 import { hasDebuggingEnabled } from "./_Polyfill";
+import { ICUDataMode } from "../../types/blazor";
 
-export async function loadBootConfig(config: MonoConfigInternal,) {
+let resourceLoader: WebAssemblyResourceLoader;
+
+export async function loadBootConfig(config: MonoConfigInternal, module: DotnetModuleInternal) {
     const candidateOptions = config.startupOptions ?? {};
     const environment = candidateOptions.environment;
     const bootConfigPromise = BootConfigResult.initAsync(candidateOptions.loadBootResource, environment);
-
     const bootConfigResult: BootConfigResult = await bootConfigPromise;
-
-    const resourceLoader = await WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, candidateOptions || {});
-
-    INTERNAL.resourceLoader = resourceLoader;
-
-    const newConfig = mapBootConfigToMonoConfig(Module.config as MonoConfigInternal, resourceLoader, bootConfigResult.applicationEnvironment);
-    Module.config = newConfig;
+    INTERNAL.resourceLoader = resourceLoader = await WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, candidateOptions || {});
+    mapBootConfigToMonoConfig(loaderHelpers.config, bootConfigResult.applicationEnvironment);
+    setupModuleForBlazor(module);
 }
 
 let resourcesLoaded = 0;
 let totalResources = 0;
 
-export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, resourceLoader: WebAssemblyResourceLoader, applicationEnvironment: string): MonoConfigInternal {
+const behaviorByName = (name: string): AssetBehaviours | "other" => {
+    return name === "dotnet.timezones.blat" ? "vfs"
+        : name === "dotnet.native.wasm" ? "dotnetwasm"
+            : (name.startsWith("dotnet.native.worker") && name.endsWith(".js")) ? "js-module-threads"
+                : (name.startsWith("dotnet.native") && name.endsWith(".js")) ? "js-module-native"
+                    : (name.startsWith("dotnet.runtime") && name.endsWith(".js")) ? "js-module-runtime"
+                        : (name.startsWith("dotnet") && name.endsWith(".js")) ? "js-module-dotnet"
+                            : name.startsWith("icudt") ? "icu"
+                                : "other";
+};
+
+const monoToBlazorAssetTypeMap: { [key: string]: WebAssemblyBootResourceType | undefined } = {
+    "assembly": "assembly",
+    "pdb": "pdb",
+    "icu": "globalization",
+    "vfs": "globalization",
+    "dotnetwasm": "dotnetwasm",
+};
+
+export function setupModuleForBlazor(module: DotnetModuleInternal) {
+    // it would not `loadResource` on types for which there is no typesMap mapping
+    const downloadResource = (asset: AssetEntry): LoadingResource | undefined => {
+        // GOTCHA: the mapping to blazor asset type may not cover all mono owned asset types in the future in which case:
+        // A) we may need to add such asset types to the mapping and to WebAssemblyBootResourceType
+        // B) or we could add generic "runtime" type to WebAssemblyBootResourceType as fallback
+        // C) or we could return `undefined` and let the runtime to load the asset. In which case the progress will not be reported on it and blazor will not be able to cache it.
+        const type = monoToBlazorAssetTypeMap[asset.behavior];
+        if (type !== undefined) {
+            const res = resourceLoader.loadResource(asset.name, asset.resolvedUrl!, asset.hash!, type);
+            asset.pendingDownload = res;
+
+            totalResources++;
+            res.response.then(() => {
+                resourcesLoaded++;
+                if (module.onDownloadResourceProgress)
+                    module.onDownloadResourceProgress(resourcesLoaded, totalResources);
+            });
+
+            return res;
+        }
+        return undefined;
+    };
+
+    module.downloadResource = downloadResource;
+    module.disableDotnet6Compatibility = false;
+}
+
+export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, applicationEnvironment: string) {
     const resources = resourceLoader.bootConfig.resources;
 
     const assets: AssetEntry[] = [];
@@ -39,10 +89,10 @@ export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, reso
     moduleConfig.enableDownloadRetry = false; // disable retry downloads
     moduleConfig.mainAssemblyName = resourceLoader.bootConfig.entryAssembly;
 
-    moduleConfig = {
+    // FIXME this mix of both formats is ugly temporary hack
+    Object.assign(moduleConfig, {
         ...resourceLoader.bootConfig,
-        ...moduleConfig
-    };
+    });
 
     if (resourceLoader.bootConfig.startupMemoryCache !== undefined) {
         moduleConfig.startupMemoryCache = resourceLoader.bootConfig.startupMemoryCache;
@@ -52,58 +102,12 @@ export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, reso
         moduleConfig.runtimeOptions = [...(moduleConfig.runtimeOptions || []), ...resourceLoader.bootConfig.runtimeOptions];
     }
 
-    const monoToBlazorAssetTypeMap: { [key: string]: WebAssemblyBootResourceType | undefined } = {
-        "assembly": "assembly",
-        "pdb": "pdb",
-        "icu": "globalization",
-        "vfs": "globalization",
-        "dotnetwasm": "dotnetwasm",
-    };
-
-    const behaviorByName = (name: string) => {
-        return name === "dotnet.timezones.blat" ? "vfs"
-            : name === "dotnet.wasm" ? "dotnetwasm"
-                : (name.startsWith("dotnet.worker") && name.endsWith(".js")) ? "js-module-threads"
-                    : (name.startsWith("dotnet") && name.endsWith(".js")) ? "js-module-dotnet"
-                        : name.startsWith("icudt") ? "icu"
-                            : "other";
-    };
-
-    // it would not `loadResource` on types for which there is no typesMap mapping
-    const downloadResource = (asset: AssetEntry): LoadingResource | undefined => {
-        // GOTCHA: the mapping to blazor asset type may not cover all mono owned asset types in the future in which case:
-        // A) we may need to add such asset types to the mapping and to WebAssemblyBootResourceType
-        // B) or we could add generic "runtime" type to WebAssemblyBootResourceType as fallback
-        // C) or we could return `undefined` and let the runtime to load the asset. In which case the progress will not be reported on it and blazor will not be able to cache it.
-        const type = monoToBlazorAssetTypeMap[asset.behavior];
-        if (type !== undefined) {
-            const res = resourceLoader.loadResource(asset.name, asset.resolvedUrl!, asset.hash!, type);
-            asset.pendingDownload = res;
-
-            totalResources++;
-            res.response.then(() => {
-                resourcesLoaded++;
-                if (Module.onDownloadResourceProgress)
-                    Module.onDownloadResourceProgress(resourcesLoaded, totalResources);
-            });
-
-            return res;
-        }
-        return undefined;
-    };
-
-    Module.downloadResource = downloadResource;
-    Module.disableDotnet6Compatibility = false;
-
     // any runtime owned assets, with proper behavior already set
     for (const name in resources.runtimeAssets) {
         const asset = resources.runtimeAssets[name] as AssetEntry;
         asset.name = name;
         asset.resolvedUrl = `_framework/${name}`;
         assets.push(asset);
-        if (asset.behavior === "dotnetwasm") {
-            downloadResource(asset);
-        }
     }
     for (const name in resources.assembly) {
         const asset: AssetEntry = {
@@ -113,7 +117,6 @@ export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, reso
             behavior: "assembly",
         };
         assets.push(asset);
-        downloadResource(asset);
     }
     if (hasDebuggingEnabled(resourceLoader.bootConfig) && resources.pdb) {
         for (const name in resources.pdb) {
@@ -124,7 +127,6 @@ export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, reso
                 behavior: "pdb",
             };
             assets.push(asset);
-            downloadResource(asset);
         }
     }
     const applicationCulture = resourceLoader.startOptions.applicationCulture || (navigator.languages && navigator.languages[0]);
@@ -145,9 +147,11 @@ export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, reso
         } else if (behavior === "dotnetwasm") {
             continue;
         }
+
+        const resolvedUrl = name.endsWith(".js") ? `./${name}` : `_framework/${name}`;
         const asset: AssetEntry = {
             name,
-            resolvedUrl: `_framework/${name}`,
+            resolvedUrl,
             hash: resources.runtime[name],
             behavior,
         };
@@ -175,8 +179,6 @@ export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, reso
     if (resourceLoader.bootConfig.runtimeOptions) {
         moduleConfig.runtimeOptions = [...(moduleConfig.runtimeOptions || []), ...(resourceLoader.bootConfig.runtimeOptions || [])];
     }
-
-    return moduleConfig;
 }
 
 function getICUResourceName(bootConfig: BootJsonData, culture: string | undefined): string {
@@ -217,4 +219,5 @@ function getICUResourceName(bootConfig: BootJsonData, culture: string | undefine
         return "icudt_CJK.dat";
     }
     return "icudt_no_CJK.dat";
-}
\ No newline at end of file
+}
+
similarity index 87%
rename from src/mono/wasm/runtime/blazor/_Polyfill.ts
rename to src/mono/wasm/runtime/loader/blazor/_Polyfill.ts
index dc4aded..4eeefe2 100644 (file)
@@ -1,4 +1,7 @@
-import { BootJsonData } from "./BootConfig";
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { BootJsonData } from "../../types/blazor";
 
 let testAnchor: HTMLAnchorElement;
 export function toAbsoluteUri(relativeUri: string): string {
diff --git a/src/mono/wasm/runtime/loader/config.ts b/src/mono/wasm/runtime/loader/config.ts
new file mode 100644 (file)
index 0000000..28fa634
--- /dev/null
@@ -0,0 +1,111 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import BuildConfiguration from "consts:configuration";
+import type { DotnetModuleInternal, MonoConfigInternal } from "../types/internal";
+import type { DotnetModuleConfig } from "../types";
+import { exportedRuntimeAPI, loaderHelpers, runtimeHelpers } from "./globals";
+import { loadBootConfig } from "./blazor/_Integration";
+
+export function deep_merge_config(target: MonoConfigInternal, source: MonoConfigInternal): MonoConfigInternal {
+    const providedConfig: MonoConfigInternal = { ...source };
+    if (providedConfig.assets) {
+        providedConfig.assets = [...(target.assets || []), ...(providedConfig.assets || [])];
+    }
+    if (providedConfig.environmentVariables) {
+        providedConfig.environmentVariables = { ...(target.environmentVariables || {}), ...(providedConfig.environmentVariables || {}) };
+    }
+    if (providedConfig.startupOptions) {
+        providedConfig.startupOptions = { ...(target.startupOptions || {}), ...(providedConfig.startupOptions || {}) };
+    }
+    if (providedConfig.runtimeOptions) {
+        providedConfig.runtimeOptions = [...(target.runtimeOptions || []), ...(providedConfig.runtimeOptions || [])];
+    }
+    return Object.assign(target, providedConfig);
+}
+
+export function deep_merge_module(target: DotnetModuleInternal, source: DotnetModuleConfig): DotnetModuleInternal {
+    const providedConfig: DotnetModuleConfig = { ...source };
+    if (providedConfig.config) {
+        if (!target.config) target.config = {};
+        providedConfig.config = deep_merge_config(target.config, providedConfig.config);
+    }
+    return Object.assign(target, providedConfig);
+}
+
+export function normalizeConfig() {
+    // normalize
+    const config = loaderHelpers.config;
+
+    config.environmentVariables = config.environmentVariables || {};
+    config.assets = config.assets || [];
+    config.runtimeOptions = config.runtimeOptions || [];
+    config.globalizationMode = config.globalizationMode || "auto";
+
+    if (config.debugLevel === undefined && BuildConfiguration === "Debug") {
+        config.debugLevel = -1;
+    }
+    if (config.diagnosticTracing === undefined && BuildConfiguration === "Debug") {
+        config.diagnosticTracing = true;
+    }
+    runtimeHelpers.diagnosticTracing = loaderHelpers.diagnosticTracing = !!config.diagnosticTracing;
+    loaderHelpers.assetUniqueQuery = config.assetUniqueQuery;
+    runtimeHelpers.waitForDebugger = config.waitForDebugger;
+    config.startupMemoryCache = !!config.startupMemoryCache;
+    if (config.startupMemoryCache && runtimeHelpers.waitForDebugger) {
+        if (loaderHelpers.diagnosticTracing) console.info("MONO_WASM: Disabling startupMemoryCache because waitForDebugger is set");
+        config.startupMemoryCache = false;
+    }
+
+    runtimeHelpers.enablePerfMeasure = !!config.browserProfilerOptions
+        && globalThis.performance
+        && typeof globalThis.performance.measure === "function";
+
+}
+
+let configLoaded = false;
+export async function mono_wasm_load_config(module: DotnetModuleInternal): Promise<void> {
+    const configFilePath = module.configSrc;
+    if (configLoaded) {
+        await loaderHelpers.afterConfigLoaded.promise;
+        return;
+    }
+    configLoaded = true;
+    if (!configFilePath) {
+        normalizeConfig();
+        loaderHelpers.afterConfigLoaded.promise_control.resolve(loaderHelpers.config);
+        return;
+    }
+    if (loaderHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_load_config");
+    try {
+        const resolveSrc = loaderHelpers.locateFile(configFilePath);
+        const configResponse = await loaderHelpers.fetch_like(resolveSrc);
+        const loadedConfig: MonoConfigInternal = (await configResponse.json()) || {};
+        if (loaderHelpers.config.startupOptions) {
+            await loadBootConfig(loaderHelpers.config, module);
+        } else {
+            if (loadedConfig.environmentVariables && typeof (loadedConfig.environmentVariables) !== "object")
+                throw new Error("Expected config.environmentVariables to be unset or a dictionary-style object");
+            deep_merge_config(loaderHelpers.config, loadedConfig);
+        }
+
+        normalizeConfig();
+
+        if (module.onConfigLoaded) {
+            try {
+                await module.onConfigLoaded(loaderHelpers.config, exportedRuntimeAPI);
+                normalizeConfig();
+            }
+            catch (err: any) {
+                console.error("MONO_WASM: onConfigLoaded() failed", err);
+                throw err;
+            }
+        }
+        loaderHelpers.afterConfigLoaded.promise_control.resolve(loaderHelpers.config);
+    } catch (err) {
+        const errMessage = `Failed to load config file ${configFilePath} ${err}`;
+        loaderHelpers.config = module.config = <any>{ message: errMessage, error: err, isError: true };
+        loaderHelpers.abort_startup(errMessage, true);
+        throw err;
+    }
+}
\ No newline at end of file
diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts
new file mode 100644 (file)
index 0000000..ff3b62b
--- /dev/null
@@ -0,0 +1,154 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, INTERNAL, loaderHelpers, runtimeHelpers } from "./globals";
+import { consoleWebSocket } from "./logging";
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function abort_startup(reason: any, should_exit: boolean): void {
+    if (loaderHelpers.diagnosticTracing) console.trace("MONO_WASM: abort_startup");
+    loaderHelpers.allDownloadsQueued.promise_control.reject(reason);
+    loaderHelpers.afterConfigLoaded.promise_control.reject(reason);
+    loaderHelpers.wasmDownloadPromise.promise_control.reject(reason);
+    loaderHelpers.runtimeModuleLoaded.promise_control.reject(reason);
+    if (runtimeHelpers.dotnetReady) {
+        runtimeHelpers.dotnetReady.promise_control.reject(reason);
+        runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.reject(reason);
+        runtimeHelpers.afterInstantiateWasm.promise_control.reject(reason);
+        runtimeHelpers.beforePreInit.promise_control.reject(reason);
+        runtimeHelpers.afterPreInit.promise_control.reject(reason);
+        runtimeHelpers.afterPreRun.promise_control.reject(reason);
+        runtimeHelpers.beforeOnRuntimeInitialized.promise_control.reject(reason);
+        runtimeHelpers.afterOnRuntimeInitialized.promise_control.reject(reason);
+        runtimeHelpers.afterPostRun.promise_control.reject(reason);
+    }
+    if (typeof reason !== "object" || reason.silent !== true) {
+        if (should_exit || ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) {
+            mono_exit(1, reason);
+        }
+        throw reason;
+    }
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export function mono_exit(exit_code: number, reason?: any): void {
+    if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) {
+        // this would NOT call Node's exit() immediately, it's a hanging promise
+        (async () => {
+            try {
+                await flush_node_streams();
+            }
+            finally {
+                set_exit_code_and_quit_now(exit_code, reason);
+            }
+        })();
+        // we need to throw, rather than let the caller continue the normal execution
+        // in the middle of some code, which expects this to stop the process
+        throw runtimeHelpers.ExitStatus
+            ? new runtimeHelpers.ExitStatus(exit_code)
+            : reason
+                ? reason
+                : new Error("Stop with exit code " + exit_code);
+    } else {
+        set_exit_code_and_quit_now(exit_code, reason);
+    }
+}
+
+async function flush_node_streams() {
+    try {
+        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+        // @ts-ignore:
+        const process = await import(/* webpackIgnore: true */"process");
+        const flushStream = (stream: any) => {
+            return new Promise<void>((resolve, reject) => {
+                stream.on("error", (error: any) => reject(error));
+                stream.write("", function () { resolve(); });
+            });
+        };
+        const stderrFlushed = flushStream(process.stderr);
+        const stdoutFlushed = flushStream(process.stdout);
+        await Promise.all([stdoutFlushed, stderrFlushed]);
+    } catch (err) {
+        console.error(`flushing std* streams failed: ${err}`);
+    }
+}
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+function set_exit_code_and_quit_now(exit_code: number, reason?: any): void {
+    if (runtimeHelpers.ExitStatus) {
+        if (reason && !(reason instanceof runtimeHelpers.ExitStatus)) {
+            if (!loaderHelpers.config.logExitCode) {
+                if (reason instanceof Error && runtimeHelpers.stringify_as_error_with_stack)
+                    loaderHelpers.err(runtimeHelpers.stringify_as_error_with_stack(reason));
+                else if (typeof reason == "string")
+                    loaderHelpers.err(reason);
+                else
+                    loaderHelpers.err(JSON.stringify(reason));
+            }
+        }
+        else if (!reason) {
+            reason = new runtimeHelpers.ExitStatus(exit_code);
+        } else if (typeof reason.status === "number") {
+            exit_code = reason.status;
+        }
+    }
+    logErrorOnExit(exit_code, reason);
+    try {
+        if (runtimeHelpers.jiterpreter_dump_stats) runtimeHelpers.jiterpreter_dump_stats(false);
+    } catch {
+        // eslint-disable-next-line @typescript-eslint/no-extra-semi
+        ;
+    }
+
+    appendElementOnExit(exit_code);
+    if (exit_code !== 0 || !ENVIRONMENT_IS_WEB) {
+        if (ENVIRONMENT_IS_NODE && INTERNAL.process) {
+            INTERNAL.process.exit(exit_code);
+            throw reason;
+        }
+        else if (runtimeHelpers.quit) {
+            runtimeHelpers.quit(exit_code, reason);
+        } else {
+            throw reason;
+        }
+    }
+}
+
+function appendElementOnExit(exit_code: number) {
+    if (ENVIRONMENT_IS_WEB && loaderHelpers.config && loaderHelpers.config.appendElementOnExit) {
+        //Tell xharness WasmBrowserTestRunner what was the exit code
+        const tests_done_elem = document.createElement("label");
+        tests_done_elem.id = "tests_done";
+        if (exit_code) tests_done_elem.style.background = "red";
+        tests_done_elem.innerHTML = exit_code.toString();
+        document.body.appendChild(tests_done_elem);
+    }
+}
+
+function logErrorOnExit(exit_code: number, reason?: any) {
+    if (loaderHelpers.config && loaderHelpers.config.logExitCode) {
+        if (exit_code != 0 && reason) {
+            if (reason instanceof Error && runtimeHelpers.stringify_as_error_with_stack)
+                console.error(runtimeHelpers.stringify_as_error_with_stack(reason));
+            else if (typeof reason == "string")
+                console.error(reason);
+            else
+                console.error(JSON.stringify(reason));
+        }
+        if (consoleWebSocket) {
+            const stop_when_ws_buffer_empty = () => {
+                if (consoleWebSocket.bufferedAmount == 0) {
+                    // tell xharness WasmTestMessagesProcessor we are done.
+                    // note this sends last few bytes into the same WS
+                    console.log("WASM EXIT " + exit_code);
+                }
+                else {
+                    setTimeout(stop_when_ws_buffer_empty, 100);
+                }
+            };
+            stop_when_ws_buffer_empty();
+        } else {
+            console.log("WASM EXIT " + exit_code);
+        }
+    }
+}
diff --git a/src/mono/wasm/runtime/loader/globals.ts b/src/mono/wasm/runtime/loader/globals.ts
new file mode 100644 (file)
index 0000000..f221d46
--- /dev/null
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { AssetEntryInternal, GlobalObjects, LoaderHelpers, RuntimeHelpers } from "../types/internal";
+import type { MonoConfig, RuntimeAPI } from "../types";
+import { abort_startup, mono_exit } from "./exit";
+import { assertIsControllablePromise, createPromiseController, getPromiseController } from "./promise-controller";
+import { mono_download_assets, resolve_asset_path } from "./assets";
+import { setup_proxy_console } from "./logging";
+
+export const ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string";
+export const ENVIRONMENT_IS_WEB = typeof window == "object";
+export const ENVIRONMENT_IS_WORKER = typeof importScripts == "function";
+export const ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
+
+export let runtimeHelpers: RuntimeHelpers = null as any;
+export let loaderHelpers: LoaderHelpers = null as any;
+export let exportedRuntimeAPI: RuntimeAPI = null as any;
+export let INTERNAL: any;
+export let _loaderModuleLoaded = false; // please keep it in place also as rollup guard
+
+export function setLoaderGlobals(
+    globalObjects: GlobalObjects,
+) {
+    if (_loaderModuleLoaded) {
+        throw new Error("Loader module already loaded");
+    }
+    _loaderModuleLoaded = true;
+    runtimeHelpers = globalObjects.runtimeHelpers;
+    loaderHelpers = globalObjects.loaderHelpers;
+    exportedRuntimeAPI = globalObjects.api;
+    INTERNAL = globalObjects.internal;
+    Object.assign(exportedRuntimeAPI, {
+        INTERNAL
+    });
+
+    Object.assign(globalObjects.module, {
+        disableDotnet6Compatibility: true,
+        config: { environmentVariables: {} }
+    });
+    Object.assign(runtimeHelpers, {
+        config: globalObjects.module.config,
+        diagnosticTracing: false,
+    });
+    Object.assign(loaderHelpers, {
+        config: globalObjects.module.config,
+        diagnosticTracing: false,
+
+        maxParallelDownloads: 16,
+        enableDownloadRetry: true,
+
+        _loaded_files: [],
+        loadedFiles: [],
+        actual_downloaded_assets_count: 0,
+        actual_instantiated_assets_count: 0,
+        expected_downloaded_assets_count: 0,
+        expected_instantiated_assets_count: 0,
+
+        afterConfigLoaded: createPromiseController<MonoConfig>(),
+        allDownloadsQueued: createPromiseController<void>(),
+        wasmDownloadPromise: createPromiseController<AssetEntryInternal>(),
+        runtimeModuleLoaded: createPromiseController<void>(),
+
+        abort_startup,
+        mono_exit,
+        createPromiseController,
+        getPromiseController,
+        assertIsControllablePromise,
+        mono_download_assets,
+        resolve_asset_path,
+        setup_proxy_console,
+
+    } as Partial<LoaderHelpers>);
+}
diff --git a/src/mono/wasm/runtime/loader/icu.ts b/src/mono/wasm/runtime/loader/icu.ts
new file mode 100644 (file)
index 0000000..39e2bcc
--- /dev/null
@@ -0,0 +1,71 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { ENVIRONMENT_IS_WEB, loaderHelpers } from "./globals";
+
+export function init_globalization() {
+    loaderHelpers.invariantMode = loaderHelpers.config.globalizationMode === "invariant";
+    loaderHelpers.preferredIcuAsset = get_preferred_icu_asset();
+
+    if (!loaderHelpers.invariantMode) {
+        if (loaderHelpers.preferredIcuAsset) {
+            if (loaderHelpers.diagnosticTracing) console.debug("MONO_WASM: ICU data archive(s) available, disabling invariant mode");
+        } else if (loaderHelpers.config.globalizationMode !== "icu") {
+            if (loaderHelpers.diagnosticTracing) console.debug("MONO_WASM: ICU data archive(s) not available, using invariant globalization mode");
+            loaderHelpers.invariantMode = true;
+            loaderHelpers.preferredIcuAsset = null;
+        } else {
+            const msg = "invariant globalization mode is inactive and no ICU data archives are available";
+            loaderHelpers.err(`MONO_WASM: ERROR: ${msg}`);
+            throw new Error(msg);
+        }
+    }
+
+    const invariantEnv = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
+    const hybridEnv = "DOTNET_SYSTEM_GLOBALIZATION_HYBRID";
+    const env_variables = loaderHelpers.config.environmentVariables!;
+    if (env_variables[hybridEnv] === undefined && loaderHelpers.config.globalizationMode === "hybrid") {
+        env_variables[hybridEnv] = "1";
+    }
+    else if (env_variables[invariantEnv] === undefined && loaderHelpers.invariantMode) {
+        env_variables[invariantEnv] = "1";
+    }
+    if (env_variables["TZ"] === undefined) {
+        try {
+            // this call is relatively expensive, so we call it during download of other assets
+            const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || null;
+            if (timezone) {
+                env_variables!["TZ"] = timezone;
+            }
+        } catch {
+            console.info("MONO_WASM: failed to detect timezone, will fallback to UTC");
+        }
+    }
+}
+
+export function get_preferred_icu_asset(): string | null {
+    if (!loaderHelpers.config.assets || loaderHelpers.invariantMode)
+        return null;
+
+    // By setting <WasmIcuDataFileName> user can define what ICU source file they want to load.
+    // There is no need to check application's culture when <WasmIcuDataFileName> is set.
+    // If it was not set, then we have 3 "icu" assets in config and we should choose
+    // only one for loading, the one that matches the application's locale.
+    const icuAssets = loaderHelpers.config.assets.filter(a => a["behavior"] == "icu");
+    if (icuAssets.length === 1)
+        return icuAssets[0].name;
+
+    // reads the browsers locale / the OS's locale
+    const preferredCulture = ENVIRONMENT_IS_WEB ? navigator.language : Intl.DateTimeFormat().resolvedOptions().locale;
+    const prefix = preferredCulture.split("-")[0];
+    const CJK = "icudt_CJK.dat";
+    const EFIGS = "icudt_EFIGS.dat";
+    const OTHERS = "icudt_no_CJK.dat";
+
+    // not all "fr-*", "it-*", "de-*", "es-*" are in EFIGS, only the one that is mostly used
+    if (prefix == "en" || ["fr", "fr-FR", "it", "it-IT", "de", "de-DE", "es", "es-ES"].includes(preferredCulture))
+        return EFIGS;
+    if (["zh", "ko", "ja"].includes(prefix))
+        return CJK;
+    return OTHERS;
+}
diff --git a/src/mono/wasm/runtime/loader/index.ts b/src/mono/wasm/runtime/loader/index.ts
new file mode 100644 (file)
index 0000000..9c2f0d0
--- /dev/null
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { DotnetHostBuilder } from "../types";
+import { mono_exit } from "./exit";
+import { HostBuilder, createEmscripten } from "./run";
+
+// export external API
+const dotnet: DotnetHostBuilder = new HostBuilder();
+const exit = mono_exit;
+const legacyEntrypoint = createEmscripten;
+
+export { dotnet, exit };
+export default legacyEntrypoint;
diff --git a/src/mono/wasm/runtime/loader/logging.ts b/src/mono/wasm/runtime/loader/logging.ts
new file mode 100644 (file)
index 0000000..7be1ec6
--- /dev/null
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+export let consoleWebSocket: WebSocket;
+
+export function setup_proxy_console(id: string, console: Console, origin: string): void {
+    // this need to be copy, in order to keep reference to original methods
+    const originalConsole = {
+        log: console.log,
+        error: console.error
+    };
+    const anyConsole = console as any;
+
+    function proxyConsoleMethod(prefix: string, func: any, asJson: boolean) {
+        return function (...args: any[]) {
+            try {
+                let payload = args[0];
+                if (payload === undefined) payload = "undefined";
+                else if (payload === null) payload = "null";
+                else if (typeof payload === "function") payload = payload.toString();
+                else if (typeof payload !== "string") {
+                    try {
+                        payload = JSON.stringify(payload);
+                    } catch (e) {
+                        payload = payload.toString();
+                    }
+                }
+
+                if (typeof payload === "string") {
+                    if (payload[0] == "[") {
+                        const now = new Date().toISOString();
+                        if (id !== "main") {
+                            payload = `[${id}][${now}] ${payload}`;
+                        } else {
+                            payload = `[${now}] ${payload}`;
+                        }
+                    } else if (id !== "main") {
+                        payload = `[${id}] ${payload}`;
+                    }
+                }
+
+                if (asJson) {
+                    func(JSON.stringify({
+                        method: prefix,
+                        payload: payload,
+                        arguments: args
+                    }));
+                } else {
+                    func([prefix + payload, ...args.slice(1)]);
+                }
+            } catch (err) {
+                originalConsole.error(`proxyConsole failed: ${err}`);
+            }
+        };
+    }
+
+    const methods = ["debug", "trace", "warn", "info", "error"];
+    for (const m of methods) {
+        if (typeof (anyConsole[m]) !== "function") {
+            anyConsole[m] = proxyConsoleMethod(`console.${m}: `, console.log, false);
+        }
+    }
+
+    const consoleUrl = `${origin}/console`.replace("https://", "wss://").replace("http://", "ws://");
+
+    consoleWebSocket = new WebSocket(consoleUrl);
+    consoleWebSocket.addEventListener("open", () => {
+        originalConsole.log(`browser: [${id}] Console websocket connected.`);
+    });
+    consoleWebSocket.addEventListener("error", (event) => {
+        originalConsole.error(`[${id}] websocket error: ${event}`, event);
+    });
+    consoleWebSocket.addEventListener("close", (event) => {
+        originalConsole.error(`[${id}] websocket closed: ${event}`, event);
+    });
+
+    const send = (msg: string) => {
+        if (consoleWebSocket.readyState === WebSocket.OPEN) {
+            consoleWebSocket.send(msg);
+        }
+        else {
+            originalConsole.log(msg);
+        }
+    };
+
+    for (const m of ["log", ...methods])
+        anyConsole[m] = proxyConsoleMethod(`console.${m}`, send, true);
+}
diff --git a/src/mono/wasm/runtime/loader/polyfills.ts b/src/mono/wasm/runtime/loader/polyfills.ts
new file mode 100644 (file)
index 0000000..dd75714
--- /dev/null
@@ -0,0 +1,124 @@
+
+import type { DotnetModuleInternal } from "../types/internal";
+import { INTERNAL, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers } from "./globals";
+
+let node_fs: any | undefined = undefined;
+let node_url: any | undefined = undefined;
+
+export async function init_polyfills(module: DotnetModuleInternal): Promise<void> {
+
+    loaderHelpers.scriptUrl = normalizeFileUrl(/* webpackIgnore: true */import.meta.url);
+    loaderHelpers.scriptDirectory = normalizeDirectoryUrl(loaderHelpers.scriptUrl);
+    loaderHelpers.locateFile = (path) => {
+        if (isPathAbsolute(path)) return path;
+        return loaderHelpers.scriptDirectory + path;
+    };
+    loaderHelpers.downloadResource = module.downloadResource;
+    loaderHelpers.fetch_like = fetch_like;
+    loaderHelpers.out = console.log;
+    loaderHelpers.err = console.error;
+    loaderHelpers.getApplicationEnvironment = module.getApplicationEnvironment;
+
+    if (ENVIRONMENT_IS_NODE) {
+        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+        // @ts-ignore:
+        INTERNAL.require = await import(/* webpackIgnore: true */"module").then(mod => mod.createRequire(/* webpackIgnore: true */import.meta.url));
+    } else {
+        INTERNAL.require = Promise.resolve(() => { throw new Error("require not supported"); });
+    }
+
+    if (typeof globalThis.URL === "undefined") {
+        globalThis.URL = class URL {
+            private url;
+            constructor(url: string) {
+                this.url = url;
+            }
+            toString() {
+                return this.url;
+            }
+        } as any;
+    }
+}
+
+const hasFetch = typeof (globalThis.fetch) === "function";
+export async function fetch_like(url: string, init?: RequestInit): Promise<Response> {
+    try {
+        if (ENVIRONMENT_IS_NODE) {
+            const isFileUrl = url.startsWith("file://");
+            if (!isFileUrl && hasFetch) {
+                return globalThis.fetch(url, init || { credentials: "same-origin" });
+            }
+            if (!node_fs) {
+                node_url = INTERNAL.require("url");
+                node_fs = INTERNAL.require("fs");
+            }
+            if (isFileUrl) {
+                url = node_url.fileURLToPath(url);
+            }
+
+            const arrayBuffer = await node_fs.promises.readFile(url);
+            return <Response><any>{
+                ok: true,
+                headers: [],
+                url,
+                arrayBuffer: () => arrayBuffer,
+                json: () => JSON.parse(arrayBuffer)
+            };
+        }
+        else if (hasFetch) {
+            return globalThis.fetch(url, init || { credentials: "same-origin" });
+        }
+        else if (typeof (read) === "function") {
+            // note that it can't open files with unicode names, like Stra<unicode char - Latin Small Letter Sharp S>e.xml
+            // https://bugs.chromium.org/p/v8/issues/detail?id=12541
+            return <Response><any>{
+                ok: true,
+                url,
+                arrayBuffer: () => {
+                    return new Uint8Array(read(url, "binary"));
+                },
+                json: () => {
+                    return JSON.parse(read(url, "utf8"));
+                }
+            };
+        }
+    }
+    catch (e: any) {
+        return <Response><any>{
+            ok: false,
+            url,
+            status: 500,
+            statusText: "ERR28: " + e,
+            arrayBuffer: () => { throw e; },
+            json: () => { throw e; }
+        };
+    }
+    throw new Error("No fetch implementation available");
+}
+
+function normalizeFileUrl(filename: string) {
+    // unix vs windows
+    // remove query string
+    return filename.replace(/\\/g, "/").replace(/[?#].*/, "");
+}
+
+function normalizeDirectoryUrl(dir: string) {
+    return dir.slice(0, dir.lastIndexOf("/")) + "/";
+}
+
+const protocolRx = /^[a-zA-Z][a-zA-Z\d+\-.]*?:\/\//;
+const windowsAbsoluteRx = /[a-zA-Z]:[\\/]/;
+function isPathAbsolute(path: string): boolean {
+    if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
+        // unix /x.json
+        // windows \x.json
+        // windows C:\x.json
+        // windows C:/x.json
+        return path.startsWith("/") || path.startsWith("\\") || path.indexOf("///") !== -1 || windowsAbsoluteRx.test(path);
+    }
+
+    // anything with protocol is always absolute
+    // windows file:///C:/x.json
+    // windows http://C:/x.json
+    return protocolRx.test(path);
+}
@@ -1,32 +1,11 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
-import {
-    mono_assert
-} from "./types";
+
+import type { ControllablePromise, PromiseAndController, PromiseController } from "../types/internal";
 
 /// a unique symbol used to mark a promise as controllable
 export const promise_control_symbol = Symbol.for("wasm promise_control");
 
-/// A PromiseController encapsulates a Promise together with easy access to its resolve and reject functions.
-/// It's a bit like a TaskCompletionSource in .NET
-export interface PromiseController<T = any> {
-    isDone: boolean;
-    readonly promise: Promise<T>;
-    resolve: (value: T | PromiseLike<T>) => void;
-    reject: (reason?: any) => void;
-}
-
-/// A Promise<T> with a controller attached
-export interface ControllablePromise<T = any> extends Promise<T> {
-    [promise_control_symbol]: PromiseController<T>;
-}
-
-/// Just a pair of a promise and its controller
-export interface PromiseAndController<T> {
-    promise: ControllablePromise<T>;
-    promise_control: PromiseController<T>;
-}
-
 /// Creates a new promise together with a controller that can be used to resolve or reject that promise.
 /// Optionally takes callbacks to be called immediately after a promise is resolved or rejected.
 export function createPromiseController<T>(afterResolve?: () => void, afterReject?: () => void): PromiseAndController<T> {
@@ -57,17 +36,17 @@ export function createPromiseController<T>(afterResolve?: () => void, afterRejec
     });
     (<any>promise_control).promise = promise;
     const controllablePromise = promise as ControllablePromise<T>;
-    controllablePromise[promise_control_symbol] = promise_control;
+    (controllablePromise as any)[promise_control_symbol] = promise_control;
     return { promise: controllablePromise, promise_control: promise_control };
 }
 
 export function getPromiseController<T>(promise: ControllablePromise<T>): PromiseController<T>;
 export function getPromiseController<T>(promise: Promise<T>): PromiseController<T> | undefined {
-    return (promise as ControllablePromise<T>)[promise_control_symbol];
+    return (promise as any)[promise_control_symbol];
 }
 
 export function isControllablePromise<T>(promise: Promise<T>): promise is ControllablePromise<T> {
-    return (promise as ControllablePromise<T>)[promise_control_symbol] !== undefined;
+    return (promise as any)[promise_control_symbol] !== undefined;
 }
 
 export function assertIsControllablePromise<T>(promise: Promise<T>): asserts promise is ControllablePromise<T> {
similarity index 75%
rename from src/mono/wasm/runtime/run-outer.ts
rename to src/mono/wasm/runtime/loader/run.ts
index 0321cd8..3c762a0 100644 (file)
@@ -1,28 +1,31 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-// WARNING: code in this file is executed before any of the emscripten code, so there is very little initialized already
+import type { MonoConfig, DotnetHostBuilder, DotnetModuleConfig, RuntimeAPI, WebAssemblyStartOptions } from "../types";
+import type { MonoConfigInternal, GlobalObjects, EmscriptenModuleInternal, RuntimeModuleExportsInternal, NativeModuleExportsInternal, } from "../types/internal";
 
-import type { MonoConfig, DotnetHostBuilder, DotnetModuleConfig, RuntimeAPI, WebAssemblyStartOptions } from "./types-api";
-import type { MonoConfigInternal, GlobalObjects, EmscriptenModuleInternal } from "./types";
-
-import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, setGlobalObjects } from "./globals";
-import { mono_exit } from "./run";
-import { mono_assert } from "./types";
+import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, exportedRuntimeAPI, setLoaderGlobals } from "./globals";
+import { deep_merge_config, deep_merge_module, mono_wasm_load_config } from "./config";
+import { mono_exit } from "./exit";
 import { setup_proxy_console } from "./logging";
-import { deep_merge_config, deep_merge_module } from "./config";
-import { initializeExports } from "./exports";
+import { resolve_asset_path, start_asset_download } from "./assets";
+import { init_polyfills } from "./polyfills";
+import { runtimeHelpers, loaderHelpers } from "./globals";
+import { init_globalization } from "./icu";
+import { setupPreloadChannelToMainThread } from "./worker";
+
 
 export const globalObjectsRoot: GlobalObjects = {
     mono: {},
     binding: {},
     internal: {},
     module: {},
-    helpers: {},
+    loaderHelpers: {},
+    runtimeHelpers: {},
     api: {}
 } as any;
 
-setGlobalObjects(globalObjectsRoot);
+setLoaderGlobals(globalObjectsRoot);
 const module = globalObjectsRoot.module;
 const monoConfig = module.config as MonoConfigInternal;
 
@@ -347,9 +350,8 @@ export class HostBuilder implements DotnetHostBuilder {
     }
 }
 
-export function unifyModuleConfig(originalModule: EmscriptenModuleInternal, moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)): DotnetModuleConfig {
-    initializeExports();
-    Object.assign(module, { ready: originalModule.ready });
+export async function createEmscripten(moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)): Promise<RuntimeAPI | EmscriptenModuleInternal> {
+    // extract ModuleConfig
     if (typeof moduleFactory === "function") {
         const extension = moduleFactory(globalObjectsRoot.api) as any;
         if (extension.ready) {
@@ -365,5 +367,78 @@ export function unifyModuleConfig(originalModule: EmscriptenModuleInternal, modu
         throw new Error("MONO_WASM: Can't use moduleFactory callback of createDotnetRuntime function.");
     }
 
+    return module.ENVIRONMENT_IS_PTHREAD
+        ? createEmscriptenWorker()
+        : createEmscriptenMain();
+}
+
+function importModules() {
+    runtimeHelpers.runtimeModuleUrl = resolve_asset_path("js-module-runtime").resolvedUrl!;
+    runtimeHelpers.nativeModuleUrl = resolve_asset_path("js-module-native").resolvedUrl!;
+    return [
+        // keep js module names dynamic by using config, in the future we can use feature detection to load different flavors
+        import(runtimeHelpers.runtimeModuleUrl),
+        import(runtimeHelpers.nativeModuleUrl),
+    ];
+}
+
+function initializeModules(es6Modules: [RuntimeModuleExportsInternal, NativeModuleExportsInternal]) {
+    const { initializeExports, initializeReplacements, configureEmscriptenStartup, configureWorkerStartup, setRuntimeGlobals, passEmscriptenInternals } = es6Modules[0];
+    const { default: emscriptenFactory } = es6Modules[1];
+    setRuntimeGlobals(globalObjectsRoot);
+    initializeExports(globalObjectsRoot);
+    loaderHelpers.runtimeModuleLoaded.promise_control.resolve();
+
+    emscriptenFactory((originalModule: EmscriptenModuleInternal) => {
+        Object.assign(module, {
+            ready: originalModule.ready,
+            __dotnet_runtime: {
+                initializeReplacements, configureEmscriptenStartup, configureWorkerStartup, passEmscriptenInternals
+            }
+        });
+
+        return module;
+    });
+}
+
+async function createEmscriptenMain(): Promise<RuntimeAPI> {
+    if (!module.configSrc && (!module.config || Object.keys(module.config).length === 0 || !module.config.assets)) {
+        // if config file location nor assets are provided
+        module.configSrc = "./mono-config.json";
+    }
+
+    await init_polyfills(module);
+
+    // download config
+    await mono_wasm_load_config(module);
+
+    const promises = importModules();
+
+    const wasmModuleAsset = resolve_asset_path("dotnetwasm");
+    start_asset_download(wasmModuleAsset).then(asset => {
+        loaderHelpers.wasmDownloadPromise.promise_control.resolve(asset);
+    });
+
+    init_globalization();
+    // TODO call mono_download_assets(); here in parallel ?
+    const es6Modules = await Promise.all(promises);
+    initializeModules(es6Modules as any);
+
+    await runtimeHelpers.dotnetReady.promise;
+
+    return exportedRuntimeAPI;
+}
+
+async function createEmscriptenWorker(): Promise<EmscriptenModuleInternal> {
+    await init_polyfills(module);
+
+    setupPreloadChannelToMainThread();
+
+    await loaderHelpers.afterConfigLoaded.promise;
+
+    const promises = importModules();
+    const es6Modules = await Promise.all(promises);
+    initializeModules(es6Modules as any);
+
     return module;
 }
diff --git a/src/mono/wasm/runtime/loader/worker.ts b/src/mono/wasm/runtime/loader/worker.ts
new file mode 100644 (file)
index 0000000..b4d6706
--- /dev/null
@@ -0,0 +1,53 @@
+import { MonoConfig } from "../types";
+import { MonoConfigInternal } from "../types/internal";
+import { deep_merge_config } from "./config";
+import { ENVIRONMENT_IS_WEB, loaderHelpers } from "./globals";
+
+export const monoSymbol = "__mono_message_please_dont_collide__"; //Symbol("mono");
+
+export function setupPreloadChannelToMainThread() {
+    const channel = new MessageChannel();
+    const workerPort = channel.port1;
+    const mainPort = channel.port2;
+    workerPort.addEventListener("message", (event) => {
+        const config = JSON.parse(event.data.config) as MonoConfig;
+        onMonoConfigReceived(config);
+        workerPort.close();
+        mainPort.close();
+    }, { once: true });
+    workerPort.start();
+    self.postMessage(makePreloadMonoMessage(mainPort), [mainPort]);
+}
+
+let workerMonoConfigReceived = false;
+
+// called when the main thread sends us the mono config
+function onMonoConfigReceived(config: MonoConfigInternal): void {
+    if (workerMonoConfigReceived) {
+        console.debug("MONO_WASM: mono config already received");
+        return;
+    }
+
+    deep_merge_config(loaderHelpers.config, config);
+    console.debug("MONO_WASM: mono config received");
+    workerMonoConfigReceived = true;
+    loaderHelpers.afterConfigLoaded.promise_control.resolve(loaderHelpers.config);
+
+    if (ENVIRONMENT_IS_WEB && config.forwardConsoleLogsToWS && typeof globalThis.WebSocket != "undefined") {
+        loaderHelpers.setup_proxy_console("pthread-worker", console, self.location.href);
+    }
+}
+
+export function makePreloadMonoMessage<TPort>(port: TPort): any {
+    return {
+        [monoSymbol]: {
+            mono_cmd: WorkerMonoCommandType.preload,
+            port
+        }
+    };
+}
+
+const enum WorkerMonoCommandType {
+    channel_created = "channel_created",
+    preload = "preload",
+}
index 5f3910e..d0f93c2 100644 (file)
@@ -106,91 +106,6 @@ export function mono_wasm_trace_logger(log_domain_ptr: CharPtr, log_level_ptr: C
     }
 }
 
-export let consoleWebSocket: WebSocket;
-
-export function setup_proxy_console(id: string, console: Console, origin: string): void {
-    // this need to be copy, in order to keep reference to original methods
-    const originalConsole = {
-        log: console.log,
-        error: console.error
-    };
-    const anyConsole = console as any;
-
-    function proxyConsoleMethod(prefix: string, func: any, asJson: boolean) {
-        return function (...args: any[]) {
-            try {
-                let payload = args[0];
-                if (payload === undefined) payload = "undefined";
-                else if (payload === null) payload = "null";
-                else if (typeof payload === "function") payload = payload.toString();
-                else if (typeof payload !== "string") {
-                    try {
-                        payload = JSON.stringify(payload);
-                    } catch (e) {
-                        payload = payload.toString();
-                    }
-                }
-
-                if (typeof payload === "string") {
-                    if (payload[0] == "[") {
-                        const now = new Date().toISOString();
-                        if (id !== "main") {
-                            payload = `[${id}][${now}] ${payload}`;
-                        } else {
-                            payload = `[${now}] ${payload}`;
-                        }
-                    } else if (id !== "main") {
-                        payload = `[${id}] ${payload}`;
-                    }
-                }
-
-                if (asJson) {
-                    func(JSON.stringify({
-                        method: prefix,
-                        payload: payload,
-                        arguments: args
-                    }));
-                } else {
-                    func([prefix + payload, ...args.slice(1)]);
-                }
-            } catch (err) {
-                originalConsole.error(`proxyConsole failed: ${err}`);
-            }
-        };
-    }
-
-    const methods = ["debug", "trace", "warn", "info", "error"];
-    for (const m of methods) {
-        if (typeof (anyConsole[m]) !== "function") {
-            anyConsole[m] = proxyConsoleMethod(`console.${m}: `, console.log, false);
-        }
-    }
-
-    const consoleUrl = `${origin}/console`.replace("https://", "wss://").replace("http://", "ws://");
-
-    consoleWebSocket = new WebSocket(consoleUrl);
-    consoleWebSocket.addEventListener("open", () => {
-        originalConsole.log(`browser: [${id}] Console websocket connected.`);
-    });
-    consoleWebSocket.addEventListener("error", (event) => {
-        originalConsole.error(`[${id}] websocket error: ${event}`, event);
-    });
-    consoleWebSocket.addEventListener("close", (event) => {
-        originalConsole.error(`[${id}] websocket closed: ${event}`, event);
-    });
-
-    const send = (msg: string) => {
-        if (consoleWebSocket.readyState === WebSocket.OPEN) {
-            consoleWebSocket.send(msg);
-        }
-        else {
-            originalConsole.log(msg);
-        }
-    };
-
-    for (const m of ["log", ...methods])
-        anyConsole[m] = proxyConsoleMethod(`console.${m}`, send, true);
-}
 
 export function parseSymbolMapFile(text: string) {
     text.split(/[\r\n]/).forEach((line: string) => {
index 4892052..bc54e51 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { GCHandle, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod, mono_assert } from "./types";
+import { GCHandle, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal";
 import cwraps from "./cwraps";
 import { runtimeHelpers, ENVIRONMENT_IS_PTHREAD, Module } from "./globals";
 import { alloc_stack_frame, get_arg, get_arg_gc_handle, set_arg_type, set_gc_handle } from "./marshal";
index cc59673..fbdd362 100644 (file)
@@ -17,7 +17,7 @@ import {
 import { get_marshaler_to_js_by_type } from "./marshal-to-js";
 import { _zero_region } from "./memory";
 import { js_string_to_mono_string_root } from "./strings";
-import { mono_assert, GCHandle, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs, MarshalerType } from "./types";
+import { GCHandle, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs, MarshalerType } from "./types/internal";
 import { TypedArray } from "./types/emscripten";
 import { addUnsettledPromise, settleUnsettledPromise } from "./pthreads/shared/eventloop";
 
index 6590bac..1cbf461 100644 (file)
@@ -1,10 +1,9 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { createPromiseController, assertIsControllablePromise, getPromiseController } from "./promise-controller";
 import cwraps from "./cwraps";
 import { _lookup_js_owned_object, mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle, setup_managed_proxy } from "./gc-handles";
-import { Module, runtimeHelpers } from "./globals";
+import { Module, createPromiseController, loaderHelpers, runtimeHelpers } from "./globals";
 import {
     ManagedObject, ManagedError,
     get_arg_gc_handle, get_arg_js_handle, get_arg_type, get_arg_i32, get_arg_f64, get_arg_i52, get_arg_i16, get_arg_u8, get_arg_f32,
@@ -14,7 +13,7 @@ import {
     ArraySegment, Span, MemoryViewType, get_signature_arg3_type, get_arg_i64_big, get_arg_intptr, get_arg_element_type, JavaScriptMarshalerArgSize
 } from "./marshal";
 import { conv_string_root } from "./strings";
-import { mono_assert, JSHandleNull, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToJs, MarshalerType } from "./types";
+import { JSHandleNull, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToJs, MarshalerType } from "./types/internal";
 import { TypedArray } from "./types/emscripten";
 import { get_marshaler_to_cs_by_type } from "./marshal-to-cs";
 
@@ -225,8 +224,8 @@ export function marshal_task_to_js(arg: JSMarshalerArgument, _?: MarshalerType,
     }
     const promise = mono_wasm_get_jsobj_from_js_handle(js_handle);
     mono_assert(!!promise, () => `ERR28: promise not found for js_handle: ${js_handle} `);
-    assertIsControllablePromise<any>(promise);
-    const promise_control = getPromiseController(promise);
+    loaderHelpers.assertIsControllablePromise<any>(promise);
+    const promise_control = loaderHelpers.getPromiseController(promise);
 
     const orig_resolve = promise_control.resolve;
     promise_control.resolve = (argInner: JSMarshalerArgument) => {
@@ -281,8 +280,8 @@ export function mono_wasm_marshal_promise(args: JSMarshalerArguments): void {
         // resolve existing promise
         const promise = mono_wasm_get_jsobj_from_js_handle(js_handle);
         mono_assert(!!promise, () => `ERR25: promise not found for js_handle: ${js_handle} `);
-        assertIsControllablePromise(promise);
-        const promise_control = getPromiseController(promise);
+        loaderHelpers.assertIsControllablePromise(promise);
+        const promise_control = loaderHelpers.getPromiseController(promise);
 
         if (exc_type !== MarshalerType.None) {
             const reason = marshal_exception_to_js(exc);
index 5cc7a9e..9fc17c7 100644 (file)
@@ -5,7 +5,7 @@ import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"
 import { Module, runtimeHelpers } from "./globals";
 import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8 } from "./memory";
 import { mono_wasm_new_external_root } from "./roots";
-import { mono_assert, GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types";
+import { GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types/internal";
 import { CharPtr, TypedArray, VoidPtr } from "./types/emscripten";
 
 export const cs_to_js_marshalers = new Map<MarshalerType, MarshalerToJs>();
index 5ab35b8..83e9417 100644 (file)
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import monoWasmThreads from "consts:monoWasmThreads";
-import { mono_assert, MemOffset, NumberOrPointer } from "./types";
+import { MemOffset, NumberOrPointer } from "./types/internal";
 import { VoidPtr, CharPtr } from "./types/emscripten";
 import cwraps, { I52Error } from "./cwraps";
 import { Module, runtimeHelpers } from "./globals";
index 63f4122..4b23e37 100644 (file)
@@ -1,5 +1,5 @@
 # Linked javascript files
-They are emcc way how to extend the dotnet.js script during linking, by appending the scripts.
+They are emcc way how to extend the dotnet.native.js script during linking, by appending the scripts.
 See https://emscripten.org/docs/tools_reference/emcc.html#emcc-pre-js
 
 There are `-extern-pre-js`,`-pre-js`, `-post-js`, `-extern-post-js`.
index 0a33b69..533a161 100644 (file)
@@ -4,7 +4,7 @@
 import { Module } from "../globals";
 import { wrap_error_root, wrap_no_error_root } from "../invoke-js";
 import { mono_wasm_new_external_root } from "../roots";
-import { MonoArray, MonoObjectRef, MonoObject } from "../types";
+import { MonoArray, MonoObjectRef, MonoObject } from "../types/internal";
 import { Int32Ptr, TypedArray } from "../types/emscripten";
 import { js_to_mono_obj_root } from "./js-to-cs";
 
index 489df81..fd4d85a 100644 (file)
@@ -1,12 +1,11 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { JSHandle, GCHandle, MonoObjectRef, MonoMethod, MonoObject, WasmRoot } from "../types";
+import { JSHandle, GCHandle, MonoObjectRef, MonoMethod, MonoObject, WasmRoot, PromiseController } from "../types/internal";
 import { mono_bind_method, _create_primitive_converters } from "./method-binding";
 import { mono_wasm_new_root } from "../roots";
 import { Module, runtimeHelpers } from "../globals";
 import cwraps from "../cwraps";
-import { PromiseController } from "../promise-controller";
 import { legacyHelpers, wasm_type_symbol } from "./globals";
 import { find_corlib_class } from "../class-loader";
 type SigLine = [lazy: boolean, jsname: string, csname: string, signature: string/*ArgsMarshalString*/];
index 2aefab4..0585daa 100644 (file)
@@ -1,21 +1,21 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+import { Int32Ptr, VoidPtr } from "../types/emscripten";
+import { MarshalType, MonoType, MarshalError, MonoTypeNull, MonoArray, MonoArrayNull, MonoObject, MonoObjectNull, GCHandle, MonoStringRef, MonoObjectRef, MonoString, JSHandleDisposed, is_nullish, WasmRoot } from "../types/internal";
 import { _are_promises_supported } from "../cancelable-promise";
 import { legacy_c_functions as cwraps } from "../cwraps";
 import { mono_wasm_get_jsobj_from_js_handle, _lookup_js_owned_object, setup_managed_proxy, mono_wasm_get_js_handle, teardown_managed_proxy, assert_not_disposed } from "../gc-handles";
 import { wrap_error_root, wrap_no_error_root } from "../invoke-js";
 import { ManagedObject } from "../marshal";
 import { getU32, getI32, getF32, getF64, setI32_unchecked } from "../memory";
-import { createPromiseController } from "../promise-controller";
 import { mono_wasm_new_root, mono_wasm_new_external_root } from "../roots";
 import { conv_string_root } from "../strings";
-import { MarshalType, MonoType, MarshalError, MonoTypeNull, MonoArray, MonoArrayNull, MonoObject, MonoObjectNull, GCHandle, MonoStringRef, MonoObjectRef, MonoString, JSHandleDisposed, is_nullish, WasmRoot } from "../types";
-import { Int32Ptr, VoidPtr } from "../types/emscripten";
 import { legacyManagedExports } from "./corebindings";
 import { legacyHelpers } from "./globals";
 import { js_to_mono_obj_root } from "./js-to-cs";
 import { mono_bind_method, mono_method_get_call_signature_ref } from "./method-binding";
+import { createPromiseController } from "../globals";
 
 const delegate_invoke_symbol = Symbol.for("wasm delegate_invoke");
 
index 9216895..7d9e4d0 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import type { MemOffset, MonoArray, MonoObject, MonoObjectRef, MonoString, NumberOrPointer, WasmRoot, WasmRootBuffer } from "../types";
+import type { MemOffset, MonoArray, MonoObject, MonoObjectRef, MonoString, NumberOrPointer, WasmRoot, WasmRootBuffer } from "../types/internal";
 import type { VoidPtr } from "../types/emscripten";
 
 /**
@@ -107,10 +107,6 @@ export type MONOType = {
      */
     mono_wasm_load_data_archive: (data: Uint8Array, prefix: string) => boolean;
     /**
-     * @deprecated Please use configSrc instead
-     */
-    mono_wasm_load_config: (configFilePath: string) => Promise<void>;
-    /**
      * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead.
      */
     mono_wasm_new_root_buffer: (capacity: number, name?: string) => WasmRootBuffer;
index 0c45c27..fc661b4 100644 (file)
@@ -4,11 +4,10 @@
 import { legacy_c_functions as cwraps } from "../cwraps";
 import { mono_wasm_runtime_ready } from "../debug";
 import { mono_wasm_load_icu_data } from "../icu";
-import { runtimeHelpers } from "../globals";
 import { mono_wasm_load_bytes_into_heap, setB32, setI8, setI16, setI32, setI52, setU52, setI64Big, setU8, setU16, setU32, setF32, setF64, getB32, getI8, getI16, getI32, getI52, getU52, getI64Big, getU8, getU16, getU32, getF32, getF64 } from "../memory";
 import { mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_new_external_root, mono_wasm_release_roots } from "../roots";
 import { mono_run_main, mono_run_main_and_exit } from "../run";
-import { mono_wasm_setenv, mono_wasm_load_config } from "../startup";
+import { mono_wasm_setenv } from "../startup";
 import { js_string_to_mono_string, conv_string, js_string_to_mono_string_root, conv_string_root } from "../strings";
 import { mono_array_to_js_array, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array } from "./cs-to-js";
 import { js_typed_array_to_array, js_to_mono_obj, js_typed_array_to_array_root, js_to_mono_obj_root } from "./js-to-cs";
@@ -17,6 +16,7 @@ import { mono_wasm_load_runtime } from "../startup";
 import { BINDINGType, MONOType } from "./export-types";
 import { mono_wasm_load_data_archive } from "../assets";
 import { mono_method_resolve } from "./method-binding";
+import { runtimeHelpers } from "../globals";
 
 export function export_mono_api(): MONOType {
     return {
@@ -26,7 +26,6 @@ export function export_mono_api(): MONOType {
         mono_wasm_load_icu_data,
         mono_wasm_runtime_ready,
         mono_wasm_load_data_archive,
-        mono_wasm_load_config,
         mono_wasm_new_root_buffer,
         mono_wasm_new_root,
         mono_wasm_new_external_root,
index cf69add..3aabbe0 100644 (file)
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import type { GlobalObjects, MonoClass } from "../types";
+import type { GlobalObjects, MonoClass } from "../types/internal";
 import type { VoidPtr } from "../types/emscripten";
 import type { BINDINGType, MONOType } from "./export-types";
 
index f4f81ef..21558c8 100644 (file)
@@ -9,7 +9,7 @@ import { wrap_error_root, wrap_no_error_root } from "../invoke-js";
 import { setI32_unchecked, setU32_unchecked, setF64, setB32 } from "../memory";
 import { mono_wasm_new_root, mono_wasm_release_roots, mono_wasm_new_external_root } from "../roots";
 import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root } from "../strings";
-import { MonoObject, is_nullish, MonoClass, MonoArray, MonoMethod, MonoObjectNull, JSHandle, MonoObjectRef, JSHandleNull, JSHandleDisposed, WasmRoot } from "../types";
+import { MonoObject, is_nullish, MonoClass, MonoArray, MonoMethod, MonoObjectNull, JSHandle, MonoObjectRef, JSHandleNull, JSHandleDisposed, WasmRoot } from "../types/internal";
 import { TypedArray, Int32Ptr } from "../types/emscripten";
 import { has_backing_array_buffer } from "./buffers";
 import { legacyManagedExports } from "./corebindings";
index 7803019..4cab645 100644 (file)
@@ -8,7 +8,7 @@ import { parseFQN } from "../invoke-cs";
 import { setI32, setU32, setF32, setF64, setU52, setI52, setB32, setI32_unchecked, setU32_unchecked, _zero_region, _create_temp_frame, getB32, getI32, getU32, getF32, getF64 } from "../memory";
 import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots";
 import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root, conv_string_root } from "../strings";
-import { MonoMethod, MonoObject, MonoType, MonoClass, mono_assert, VoidPtrNull, MarshalType, MonoString, MonoObjectNull, WasmRootBuffer, WasmRoot } from "../types";
+import { MonoMethod, MonoObject, MonoType, MonoClass, VoidPtrNull, MarshalType, MonoString, MonoObjectNull, WasmRootBuffer, WasmRoot } from "../types/internal";
 import { VoidPtr } from "../types/emscripten";
 import { legacyManagedExports } from "./corebindings";
 import { get_js_owned_object_by_gc_handle_ref, _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js";
index cf9fee9..330e7c0 100644 (file)
@@ -9,7 +9,7 @@ import { _release_temp_frame } from "../memory";
 import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots";
 import { find_entry_point } from "../run";
 import { conv_string_root, js_string_to_mono_string_root } from "../strings";
-import { JSHandle, MonoStringRef, MonoObjectRef, MonoArray, MonoString, MonoObject, is_nullish, mono_assert, WasmRoot } from "../types";
+import { JSHandle, MonoStringRef, MonoObjectRef, MonoArray, MonoString, MonoObject, is_nullish, WasmRoot } from "../types/internal";
 import { Int32Ptr, VoidPtr } from "../types/emscripten";
 import { mono_array_root_to_js_array, unbox_mono_obj_root } from "./cs-to-js";
 import { js_array_to_mono_array, js_to_mono_obj_root } from "./js-to-cs";
index 4edfffe..597821b 100644 (file)
@@ -3,31 +3,56 @@
 
 import BuildConfiguration from "consts:configuration";
 import MonoWasmThreads from "consts:monoWasmThreads";
-import type { DotnetModuleConfigImports, EmscriptenReplacements } from "./types";
+import type { EmscriptenReplacements } from "./types/internal";
 import type { TypedArray } from "./types/emscripten";
-import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WORKER, ENVIRONMENT_IS_WEB, INTERNAL, Module, runtimeHelpers } from "./globals";
+import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, INTERNAL, Module, loaderHelpers, runtimeHelpers } from "./globals";
 import { replaceEmscriptenPThreadLibrary } from "./pthreads/shared/emscripten-replacements";
 
-let node_fs: any | undefined = undefined;
-let node_url: any | undefined = undefined;
-
-export function init_polyfills(): void {
+const dummyPerformance = {
+    now: function () {
+        return Date.now();
+    }
+};
 
+export function initializeReplacements(replacements: EmscriptenReplacements): void {
     // performance.now() is used by emscripten and doesn't work in JSC
     if (typeof globalThis.performance === "undefined") {
         globalThis.performance = dummyPerformance as any;
     }
-    if (typeof globalThis.URL === "undefined") {
-        globalThis.URL = class URL {
-            private url;
-            constructor(url: string) {
-                this.url = url;
-            }
-            toString() {
-                return this.url;
-            }
-        } as any;
+    replacements.require = INTERNAL.require;
+
+    // script location
+    replacements.scriptDirectory = loaderHelpers.scriptDirectory;
+    if (Module.locateFile === Module.__locateFile) {
+        Module.locateFile = loaderHelpers.locateFile;
+    }
+
+    if (BuildConfiguration === "Debug") {
+        console.debug(`MONO_WASM: starting script ${loaderHelpers.scriptUrl}`);
+        console.debug(`MONO_WASM: starting in ${loaderHelpers.scriptDirectory}`);
+    }
+
+    // prefer fetch_like over global fetch for assets
+    replacements.fetch = loaderHelpers.fetch_like;
+
+    // misc
+    replacements.noExitRuntime = ENVIRONMENT_IS_WEB;
+
+    // threads
+    if (MonoWasmThreads) {
+        if (replacements.pthreadReplacements) {
+            replaceEmscriptenPThreadLibrary(replacements.pthreadReplacements);
+        }
     }
+
+    // memory
+    const originalUpdateMemoryViews = replacements.updateMemoryViews;
+    runtimeHelpers.updateMemoryViews = replacements.updateMemoryViews = () => {
+        originalUpdateMemoryViews();
+    };
+}
+
+export async function init_polyfills_async(): Promise<void> {
     // v8 shell doesn't have Event and EventTarget
     if (MonoWasmThreads && typeof globalThis.Event === "undefined") {
         globalThis.Event = class Event {
@@ -119,73 +144,8 @@ export function init_polyfills(): void {
             }
         };
     }
-}
-
-export function initializeReplacements(replacements: EmscriptenReplacements): void {
-    // require replacement
-    const imports = Module.imports = (Module.imports || {}) as DotnetModuleConfigImports;
-    const requireWrapper = (wrappedRequire: Function) => (name: string) => {
-        const resolved = (<any>Module.imports)[name];
-        if (resolved) {
-            return resolved;
-        }
-        return wrappedRequire(name);
-    };
-    if (imports.require) {
-        runtimeHelpers.requirePromise = replacements.requirePromise = Promise.resolve(requireWrapper(imports.require));
-    }
-    else if (replacements.require) {
-        runtimeHelpers.requirePromise = replacements.requirePromise = Promise.resolve(requireWrapper(replacements.require));
-    } else if (replacements.requirePromise) {
-        runtimeHelpers.requirePromise = replacements.requirePromise.then(require => requireWrapper(require));
-    } else {
-        runtimeHelpers.requirePromise = replacements.requirePromise = Promise.resolve(requireWrapper((name: string) => {
-            throw new Error(`Please provide Module.imports.${name} or Module.imports.require`);
-        }));
-    }
-
-    // script location
-    runtimeHelpers.scriptDirectory = replacements.scriptDirectory = detectScriptDirectory(replacements);
-    Module.mainScriptUrlOrBlob = replacements.scriptUrl;// this is needed by worker threads
-    if (BuildConfiguration === "Debug") {
-        console.debug(`MONO_WASM: starting script ${replacements.scriptUrl}`);
-        console.debug(`MONO_WASM: starting in ${runtimeHelpers.scriptDirectory}`);
-    }
-    if (Module.__locateFile === Module.locateFile) {
-        // above it's our early version, we could replace it with better
-        Module.locateFile = runtimeHelpers.locateFile = (path) => {
-            if (isPathAbsolute(path)) return path;
-            return runtimeHelpers.scriptDirectory + path;
-        };
-    } else {
-        // we use what was given to us
-        runtimeHelpers.locateFile = Module.locateFile!;
-    }
-
-    // prefer fetch_like over global fetch for assets
-    replacements.fetch = runtimeHelpers.fetch_like = imports.fetch || fetch_like;
-
-    // misc
-    replacements.noExitRuntime = ENVIRONMENT_IS_WEB;
-
-    // threads
-    if (MonoWasmThreads) {
-        if (replacements.pthreadReplacements) {
-            replaceEmscriptenPThreadLibrary(replacements.pthreadReplacements);
-        }
-    }
-
-    // memory
-    const originalUpdateMemoryViews = replacements.updateMemoryViews;
-    runtimeHelpers.updateMemoryViews = replacements.updateMemoryViews = () => {
-        originalUpdateMemoryViews();
-    };
-}
-
-export async function init_polyfills_async(): Promise<void> {
     if (ENVIRONMENT_IS_NODE) {
         // wait for locateFile setup on NodeJs
-        INTERNAL.require = await runtimeHelpers.requirePromise;
         if (globalThis.performance === dummyPerformance) {
             const { performance } = INTERNAL.require("perf_hooks");
             globalThis.performance = performance;
@@ -223,106 +183,4 @@ export async function init_polyfills_async(): Promise<void> {
     runtimeHelpers.subtle = globalThis.crypto?.subtle;
 }
 
-const dummyPerformance = {
-    now: function () {
-        return Date.now();
-    }
-};
 
-export async function fetch_like(url: string, init?: RequestInit): Promise<Response> {
-    const imports = Module.imports as DotnetModuleConfigImports;
-    const hasFetch = typeof (globalThis.fetch) === "function";
-    try {
-        if (typeof (imports.fetch) === "function") {
-            return imports.fetch(url, init || { credentials: "same-origin" });
-        }
-        else if (ENVIRONMENT_IS_NODE) {
-            const isFileUrl = url.startsWith("file://");
-            if (!isFileUrl && hasFetch) {
-                return globalThis.fetch(url, init || { credentials: "same-origin" });
-            }
-            if (!node_fs) {
-                const node_require = await runtimeHelpers.requirePromise;
-                node_url = node_require("url");
-                node_fs = node_require("fs");
-            }
-            if (isFileUrl) {
-                url = node_url.fileURLToPath(url);
-            }
-
-            const arrayBuffer = await node_fs.promises.readFile(url);
-            return <Response><any>{
-                ok: true,
-                headers: [],
-                url,
-                arrayBuffer: () => arrayBuffer,
-                json: () => JSON.parse(arrayBuffer)
-            };
-        }
-        else if (hasFetch) {
-            return globalThis.fetch(url, init || { credentials: "same-origin" });
-        }
-        else if (typeof (read) === "function") {
-            // note that it can't open files with unicode names, like Stra<unicode char - Latin Small Letter Sharp S>e.xml
-            // https://bugs.chromium.org/p/v8/issues/detail?id=12541
-            const arrayBuffer = new Uint8Array(read(url, "binary"));
-            return <Response><any>{
-                ok: true,
-                url,
-                arrayBuffer: () => arrayBuffer,
-                json: () => JSON.parse(Module.UTF8ArrayToString(arrayBuffer, 0, arrayBuffer.length))
-            };
-        }
-    }
-    catch (e: any) {
-        return <Response><any>{
-            ok: false,
-            url,
-            status: 500,
-            statusText: "ERR28: " + e,
-            arrayBuffer: () => { throw e; },
-            json: () => { throw e; }
-        };
-    }
-    throw new Error("No fetch implementation available");
-}
-
-function normalizeFileUrl(filename: string) {
-    // unix vs windows
-    // remove query string
-    return filename.replace(/\\/g, "/").replace(/[?#].*/, "");
-}
-
-function normalizeDirectoryUrl(dir: string) {
-    return dir.slice(0, dir.lastIndexOf("/")) + "/";
-}
-
-export function detectScriptDirectory(replacements: EmscriptenReplacements): string {
-    if (ENVIRONMENT_IS_WORKER) {
-        // Check worker, not web, since window could be polyfilled
-        replacements.scriptUrl = self.location.href;
-    }
-    if (!replacements.scriptUrl) {
-        // probably V8 shell in non ES6
-        replacements.scriptUrl = "./dotnet.js";
-    }
-    replacements.scriptUrl = normalizeFileUrl(replacements.scriptUrl);
-    return normalizeDirectoryUrl(replacements.scriptUrl);
-}
-
-const protocolRx = /^[a-zA-Z][a-zA-Z\d+\-.]*?:\/\//;
-const windowsAbsoluteRx = /[a-zA-Z]:[\\/]/;
-function isPathAbsolute(path: string): boolean {
-    if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
-        // unix /x.json
-        // windows \x.json
-        // windows C:\x.json
-        // windows C:/x.json
-        return path.startsWith("/") || path.startsWith("\\") || path.indexOf("///") !== -1 || windowsAbsoluteRx.test(path);
-    }
-
-    // anything with protocol is always absolute
-    // windows file:///C:/x.json
-    // windows http://C:/x.json
-    return protocolRx.test(path);
-}
index fbaa646..021273c 100644 (file)
@@ -2,9 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import { ENVIRONMENT_IS_WEB, Module, runtimeHelpers } from "./globals";
-import { AOTProfilerOptions, BrowserProfilerOptions } from "./types";
+import { MonoMethod, AOTProfilerOptions, BrowserProfilerOptions } from "./types/internal";
 import cwraps from "./cwraps";
-import { MonoMethod } from "./types";
 
 // Initialize the AOT profiler with OPTIONS.
 // Requires the AOT profiler to be linked into the app.
diff --git a/src/mono/wasm/runtime/promise-utils.ts b/src/mono/wasm/runtime/promise-utils.ts
deleted file mode 100644 (file)
index 6b1e57e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/// Make a promise that resolves after a given number of milliseconds.
-export function delay(ms: number): Promise<void> {
-    return new Promise(resolve => setTimeout(resolve, ms));
-}
-
index 9910fce..0fbefa9 100644 (file)
@@ -4,11 +4,10 @@
 import { isMonoWorkerMessageChannelCreated, monoSymbol, makeMonoThreadMessageApplyMonoConfig, isMonoWorkerMessagePreload, MonoWorkerMessage } from "../shared";
 import { pthread_ptr } from "../shared/types";
 import { MonoThreadMessage } from "../shared";
-import { PromiseController, createPromiseController } from "../../promise-controller";
-import { mono_assert } from "../../types";
 import Internals from "../shared/emscripten-internals";
-import { runtimeHelpers } from "../../globals";
-import { MonoConfig } from "../../types-api";
+import { createPromiseController, runtimeHelpers } from "../../globals";
+import { PromiseController } from "../../types/internal";
+import { MonoConfig } from "../../types";
 
 const threads: Map<pthread_ptr, Thread> = new Map();
 
index 681a64b..be5962e 100644 (file)
@@ -2,13 +2,11 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import MonoWasmThreads from "consts:monoWasmThreads";
-import { PThreadReplacements } from "../../types";
 import { afterLoadWasmModuleToWorker } from "../browser";
 import { afterThreadInitTLS } from "../worker";
 import Internals from "./emscripten-internals";
-import { resolve_asset_path } from "../../assets";
-import { mono_assert } from "../../types";
-import { runtimeHelpers } from "../../globals";
+import { loaderHelpers, runtimeHelpers } from "../../globals";
+import { PThreadReplacements } from "../../types/internal";
 
 /** @module emscripten-replacements Replacements for individual functions in the emscripten PThreads library.
  * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with
@@ -37,7 +35,7 @@ export function replaceEmscriptenPThreadLibrary(replacements: PThreadReplacement
 function replacementAllocateUnusedWorker(): void {
     if (runtimeHelpers.diagnosticTracing)
         console.debug("MONO_WASM: replacementAllocateUnusedWorker");
-    const asset = resolve_asset_path("js-module-threads");
+    const asset = loaderHelpers.resolve_asset_path("js-module-threads");
     const uri = asset.resolvedUrl;
     mono_assert(uri !== undefined, "could not resolve the uri for the js-module-threads asset");
     const worker = new Worker(uri);
index e5a8d65..73cb190 100644 (file)
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import { Module } from "../../globals";
-import { MonoConfig } from "../../types-api";
+import { MonoConfig } from "../../types";
 import { pthread_ptr } from "./types";
 
 export interface PThreadInfo {
@@ -105,15 +105,6 @@ export function makeChannelCreatedMonoMessage<TPort>(thread_id: pthread_ptr, por
     };
 }
 
-export function makePreloadMonoMessage<TPort>(port: TPort): MonoWorkerMessagePreload<TPort> {
-    return {
-        [monoSymbol]: {
-            mono_cmd: WorkerMonoCommandType.preload,
-            port
-        }
-    };
-}
-
 export function isMonoWorkerMessage(message: unknown): message is MonoWorkerMessage<any> {
     return message !== undefined && typeof message === "object" && message !== null && monoSymbol in message;
 }
index 5669377..8d8c7e6 100644 (file)
@@ -4,10 +4,10 @@
 /// <reference lib="webworker" />
 
 import MonoWasmThreads from "consts:monoWasmThreads";
-import { Module, ENVIRONMENT_IS_PTHREAD, runtimeHelpers, ENVIRONMENT_IS_WEB } from "../../globals";
-import { makeChannelCreatedMonoMessage, makePreloadMonoMessage } from "../shared";
+import { Module, ENVIRONMENT_IS_PTHREAD, runtimeHelpers } from "../../globals";
+import { makeChannelCreatedMonoMessage } from "../shared";
 import type { pthread_ptr } from "../shared/types";
-import { is_nullish, MonoConfigInternal, mono_assert } from "../../types";
+import { is_nullish } from "../../types/internal";
 import type { MonoThreadMessage } from "../shared";
 import {
     PThreadSelf,
@@ -16,9 +16,7 @@ import {
     dotnetPthreadAttached,
     WorkerThreadEventTarget
 } from "./events";
-import { setup_proxy_console } from "../../logging";
-import { afterConfigLoaded, preRunWorker } from "../../startup";
-import { MonoConfig } from "../../types-api";
+import { preRunWorker } from "../../startup";
 
 // re-export some of the events types
 export {
@@ -64,19 +62,6 @@ function monoDedicatedChannelMessageFromMainToWorker(event: MessageEvent<string>
     console.debug("MONO_WASM: got message from main on the dedicated channel", event.data);
 }
 
-export function setupPreloadChannelToMainThread() {
-    const channel = new MessageChannel();
-    const workerPort = channel.port1;
-    const mainPort = channel.port2;
-    workerPort.addEventListener("message", (event) => {
-        const config = JSON.parse(event.data.config) as MonoConfig;
-        onMonoConfigReceived(config);
-        workerPort.close();
-        mainPort.close();
-    }, { once: true });
-    workerPort.start();
-    self.postMessage(makePreloadMonoMessage(mainPort), [mainPort]);
-}
 
 function setupChannelToMainThread(pthread_ptr: pthread_ptr): PThreadSelf {
     console.debug("MONO_WASM: creating a channel", pthread_ptr);
@@ -90,25 +75,6 @@ function setupChannelToMainThread(pthread_ptr: pthread_ptr): PThreadSelf {
     return pthread_self;
 }
 
-let workerMonoConfigReceived = false;
-
-// called when the main thread sends us the mono config
-function onMonoConfigReceived(config: MonoConfigInternal): void {
-    if (workerMonoConfigReceived) {
-        console.debug("MONO_WASM: mono config already received");
-        return;
-    }
-
-    console.debug("MONO_WASM: mono config received");
-    config = runtimeHelpers.config = Module.config = Object.assign(Module.config || {} as any, config);
-    workerMonoConfigReceived = true;
-
-    afterConfigLoaded.promise_control.resolve(config);
-
-    if (ENVIRONMENT_IS_WEB && config.forwardConsoleLogsToWS && typeof globalThis.WebSocket != "undefined") {
-        setup_proxy_console("pthread-worker", console, self.location.href);
-    }
-}
 
 /// This is an implementation detail function.
 /// Called in the worker thread from mono when a pthread becomes attached to the mono runtime.
index a666eeb..dd8c7b8 100644 (file)
@@ -31,16 +31,11 @@ const terserConfig = {
         keep_fnames: /(mono_wasm_runtime_ready|mono_wasm_fire_debugger_agent_message_with_data|mono_wasm_fire_debugger_agent_message_with_data_to_pause|mono_wasm_set_timeout_exec)/,
         keep_classnames: /(ManagedObject|ManagedError|Span|ArraySegment|WasmRootBuffer|SessionOptionsBuilder)/,
     },
-    format: {
-        wrap_iife: true
-    }
 };
 const plugins = isDebug ? [writeOnChangePlugin()] : [terser(terserConfig), writeOnChangePlugin()];
 const banner = "//! Licensed to the .NET Foundation under one or more agreements.\n//! The .NET Foundation licenses this file to you under the MIT license.\n";
 const banner_dts = banner + "//!\n//! This is generated file, see src/mono/wasm/runtime/rollup.config.js\n\n//! This is not considered public API with backward compatibility guarantees. \n";
 // emcc doesn't know how to load ES6 module, that's why we need the whole rollup.js
-const format = "iife";
-const name = "__dotnet_runtime";
 const inlineAssert = [
     {
         pattern: /mono_assert\(([^,]*), *"([^"]*)"\);/gm,
@@ -50,10 +45,24 @@ const inlineAssert = [
     {
         pattern: /mono_assert\(([^,]*), \(\) => *`([^`]*)`\);/gm,
         replacement: "if (!($1)) throw new Error(`Assert failed: $2`); // inlined mono_assert"
-    }, {
-        pattern: /^\s*mono_assert/gm,
-        failure: "previous regexp didn't inline all mono_assert statements"
-    }];
+    }
+];
+const checkAssert =
+{
+    pattern: /^\s*mono_assert/gm,
+    failure: "previous regexp didn't inline all mono_assert statements"
+};
+const checkNoLoader =
+{
+    pattern: /_loaderModuleLoaded/gm,
+    failure: "module should not contain loaderModuleLoaded member. This is probably duplicated code in the output caused by a dependency outside on the loader module."
+};
+const checkNoRuntime =
+{
+    pattern: /_runtimeModuleLoaded/gm,
+    failure: "module should not contain runtimeModuleLoaded member. This is probably duplicated code in the output caused by a dependency on the runtime module."
+};
+
 
 let gitHash;
 try {
@@ -85,29 +94,26 @@ const typescriptConfigOptions = {
     include: ["**/*.ts", "../../../../artifacts/bin/native/generated/**/*.ts"]
 };
 
-const outputCodePlugins = [regexReplace(inlineAssert), consts({ productVersion, configuration, monoWasmThreads, monoDiagnosticsMock, gitHash, WasmEnableLegacyJsInterop }), typescript(typescriptConfigOptions)];
-
-const externalDependencies = [
-];
+const outputCodePlugins = [consts({ productVersion, configuration, monoWasmThreads, monoDiagnosticsMock, gitHash, WasmEnableLegacyJsInterop }), typescript(typescriptConfigOptions)];
+const externalDependencies = ["module"];
 
-const iffeConfig = {
+const loaderConfig = {
     treeshake: !isDebug,
-    input: "exports.ts",
+    input: "./loader/index.ts",
     output: [
         {
-            file: nativeBinDir + "/src/es6/runtime.es6.iffe.js",
-            name,
+            format: "es",
+            file: nativeBinDir + "/dotnet.js",
             banner,
-            format,
             plugins,
         }
     ],
     external: externalDependencies,
-    plugins: outputCodePlugins,
+    plugins: [regexReplace(inlineAssert), regexCheck([checkAssert, checkNoRuntime]), ...outputCodePlugins],
     onwarn: onwarn
 };
 const typesConfig = {
-    input: "./export-types.ts",
+    input: "./types/export-types.ts",
     output: [
         {
             format: "es",
@@ -119,6 +125,21 @@ const typesConfig = {
     external: externalDependencies,
     plugins: [dts()],
 };
+const runtimeConfig = {
+    treeshake: !isDebug,
+    input: "exports.ts",
+    output: [
+        {
+            format: "es",
+            file: nativeBinDir + "/dotnet.runtime.js",
+            banner,
+            plugins,
+        }
+    ],
+    external: externalDependencies,
+    plugins: [regexReplace(inlineAssert), regexCheck([checkAssert, checkNoLoader]), ...outputCodePlugins],
+    onwarn: onwarn
+};
 const legacyConfig = {
     input: "./net6-legacy/export-types.ts",
     output: [
@@ -189,7 +210,8 @@ function makeWorkerConfig(workerName, workerInputSourcePath) {
 const workerConfigs = findWebWorkerInputs("./workers").map((workerInput) => makeWorkerConfig(workerInput.workerName, workerInput.path));
 
 const allConfigs = [
-    iffeConfig,
+    loaderConfig,
+    runtimeConfig,
     typesConfig,
     legacyConfig,
 ].concat(workerConfigs)
@@ -254,11 +276,45 @@ function checkFileExists(file) {
         .catch(() => false);
 }
 
+function regexCheck(checks = []) {
+    const filter = createFilter("**/*.ts");
+
+    return {
+        name: "regexCheck",
+
+        renderChunk(code, chunk) {
+            const id = chunk.fileName;
+            if (!filter(id)) return null;
+            return executeCheck(this, code, id);
+        },
+
+        transform(code, id) {
+            if (!filter(id)) return null;
+            return executeCheck(this, code, id);
+        }
+    };
+
+    function executeCheck(self, code, id) {
+        // self.warn("executeCheck" + id);
+        for (const rep of checks) {
+            const { pattern, failure } = rep;
+            const match = pattern.test(code);
+            if (match) {
+                self.error(failure + " " + id);
+                return null;
+            }
+        }
+
+        return null;
+    }
+}
+
+
 function regexReplace(replacements = []) {
     const filter = createFilter("**/*.ts");
 
     return {
-        name: "replace",
+        name: "regexReplace",
 
         renderChunk(code, chunk) {
             const id = chunk.fileName;
@@ -272,21 +328,12 @@ function regexReplace(replacements = []) {
         }
     };
 
-    function executeReplacement(self, code, id) {
+    function executeReplacement(_, code) {
         // TODO use MagicString for sourcemap support
         let fixed = code;
         for (const rep of replacements) {
-            const { pattern, replacement, failure } = rep;
-            if (failure) {
-                const match = pattern.test(fixed);
-                if (match) {
-                    self.error(failure + " " + id, pattern.lastIndex);
-                    return null;
-                }
-            }
-            else {
-                fixed = fixed.replace(pattern, replacement);
-            }
+            const { pattern, replacement } = rep;
+            fixed = fixed.replace(pattern, replacement);
         }
 
         if (fixed == code) {
index 05b49a0..30d0bdc 100644 (file)
@@ -4,7 +4,7 @@
 import cwraps from "./cwraps";
 import { Module } from "./globals";
 import { VoidPtr, ManagedPointer, NativePointer } from "./types/emscripten";
-import { MonoObjectRef, MonoObjectRefNull, MonoObject, is_nullish, WasmRoot, WasmRootBuffer } from "./types";
+import { MonoObjectRef, MonoObjectRefNull, MonoObject, is_nullish, WasmRoot, WasmRootBuffer } from "./types/internal";
 import { _zero_region } from "./memory";
 
 const maxScratchRoots = 8192;
index 9b92f33..bdbb2f8 100644 (file)
@@ -1,14 +1,11 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, INTERNAL, Module, runtimeHelpers } from "./globals";
+import { loaderHelpers, runtimeHelpers } from "./globals";
 import { mono_wasm_wait_for_debugger } from "./debug";
-import { abort_startup, mono_wasm_set_main_args } from "./startup";
+import { mono_wasm_set_main_args } from "./startup";
 import cwraps from "./cwraps";
 import { assembly_load } from "./class-loader";
-import { mono_assert } from "./types";
-import { consoleWebSocket, mono_wasm_stringify_as_error_with_stack } from "./logging";
-import { jiterpreter_dump_stats } from "./jiterpreter";
 
 /**
  * Possible signatures are described here  https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/main-command-line
@@ -16,13 +13,13 @@ import { jiterpreter_dump_stats } from "./jiterpreter";
 export async function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise<number> {
     try {
         const result = await mono_run_main(main_assembly_name, args);
-        mono_exit(result);
+        loaderHelpers.mono_exit(result);
         return result;
     } catch (error) {
         if (error instanceof runtimeHelpers.ExitStatus) {
             return error.status;
         }
-        mono_exit(1, error);
+        loaderHelpers.mono_exit(1, error);
         return 1;
     }
 }
@@ -56,131 +53,3 @@ export function find_entry_point(assembly: string) {
     return method;
 }
 
-// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function mono_on_abort(error: any): void {
-    abort_startup(error, false);
-    mono_exit(1, error);
-}
-
-// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function mono_exit(exit_code: number, reason?: any): void {
-    if (runtimeHelpers.config && runtimeHelpers.config.asyncFlushOnExit && exit_code === 0) {
-        // this would NOT call Node's exit() immediately, it's a hanging promise
-        (async () => {
-            try {
-                await flush_node_streams();
-            }
-            finally {
-                set_exit_code_and_quit_now(exit_code, reason);
-            }
-        })();
-        // we need to throw, rather than let the caller continue the normal execution
-        // in the middle of some code, which expects this to stop the process
-        throw runtimeHelpers.ExitStatus
-            ? new runtimeHelpers.ExitStatus(exit_code)
-            : reason
-                ? reason
-                : new Error("Stop with exit code " + exit_code);
-    } else {
-        set_exit_code_and_quit_now(exit_code, reason);
-    }
-}
-
-async function flush_node_streams() {
-    try {
-        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-        // @ts-ignore:
-        const process = await import(/* webpackIgnore: true */"process");
-        const flushStream = (stream: any) => {
-            return new Promise<void>((resolve, reject) => {
-                stream.on("error", (error: any) => reject(error));
-                stream.write("", function () { resolve(); });
-            });
-        };
-        const stderrFlushed = flushStream(process.stderr);
-        const stdoutFlushed = flushStream(process.stdout);
-        await Promise.all([stdoutFlushed, stderrFlushed]);
-    } catch (err) {
-        console.error(`flushing std* streams failed: ${err}`);
-    }
-}
-
-// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-function set_exit_code_and_quit_now(exit_code: number, reason?: any): void {
-    if (runtimeHelpers.ExitStatus) {
-        if (reason && !(reason instanceof runtimeHelpers.ExitStatus)) {
-            if (!runtimeHelpers.config.logExitCode) {
-                if (reason instanceof Error)
-                    Module.err(mono_wasm_stringify_as_error_with_stack(reason));
-                else if (typeof reason == "string")
-                    Module.err(reason);
-                else
-                    Module.err(JSON.stringify(reason));
-            }
-        }
-        else if (!reason) {
-            reason = new runtimeHelpers.ExitStatus(exit_code);
-        } else if (typeof reason.status === "number") {
-            exit_code = reason.status;
-        }
-    }
-    logErrorOnExit(exit_code, reason);
-    appendElementOnExit(exit_code);
-    if (exit_code !== 0 || !ENVIRONMENT_IS_WEB) {
-        if (ENVIRONMENT_IS_NODE && INTERNAL.process) {
-            INTERNAL.process.exit(exit_code);
-            throw reason;
-        }
-        else if (runtimeHelpers.quit) {
-            runtimeHelpers.quit(exit_code, reason);
-        } else {
-            throw reason;
-        }
-    }
-}
-
-function appendElementOnExit(exit_code: number) {
-    if (ENVIRONMENT_IS_WEB && runtimeHelpers.config && runtimeHelpers.config.appendElementOnExit) {
-        //Tell xharness WasmBrowserTestRunner what was the exit code
-        const tests_done_elem = document.createElement("label");
-        tests_done_elem.id = "tests_done";
-        if (exit_code) tests_done_elem.style.background = "red";
-        tests_done_elem.innerHTML = exit_code.toString();
-        document.body.appendChild(tests_done_elem);
-    }
-}
-
-function logErrorOnExit(exit_code: number, reason?: any) {
-    if (runtimeHelpers.config && runtimeHelpers.config.logExitCode) {
-        if (exit_code != 0 && reason) {
-            if (reason instanceof Error)
-                console.error(mono_wasm_stringify_as_error_with_stack(reason));
-            else if (typeof reason == "string")
-                console.error(reason);
-            else
-                console.error(JSON.stringify(reason));
-        }
-        if (consoleWebSocket) {
-            const stop_when_ws_buffer_empty = () => {
-                if (consoleWebSocket.bufferedAmount == 0) {
-                    // tell xharness WasmTestMessagesProcessor we are done.
-                    // note this sends last few bytes into the same WS
-                    console.log("WASM EXIT " + exit_code);
-                }
-                else {
-                    setTimeout(stop_when_ws_buffer_empty, 100);
-                }
-            };
-            stop_when_ws_buffer_empty();
-        } else {
-            console.log("WASM EXIT " + exit_code);
-        }
-    }
-
-    try {
-        jiterpreter_dump_stats(false);
-    } catch {
-        // eslint-disable-next-line @typescript-eslint/no-extra-semi
-        ;
-    }
-}
index cf22591..02b9e45 100644 (file)
@@ -4,7 +4,7 @@
 import ProductVersion from "consts:productVersion";
 import GitHash from "consts:gitHash";
 import MonoWasmThreads from "consts:monoWasmThreads";
-import { ENVIRONMENT_IS_WEB, runtimeHelpers } from "./globals";
+import { ENVIRONMENT_IS_WEB, loaderHelpers, runtimeHelpers } from "./globals";
 
 const memoryPrefix = "https://dotnet.generated.invalid/wasm-memory";
 
@@ -159,7 +159,7 @@ async function getCacheKey(): Promise<string | null> {
     // Now we remove assets collection from the hash.
     delete inputs.assets;
     // some things are calculated at runtime, so we need to add them to the hash
-    inputs.preferredIcuAsset = runtimeHelpers.preferredIcuAsset;
+    inputs.preferredIcuAsset = loaderHelpers.preferredIcuAsset;
     // timezone is part of env variables, so it is already in the hash
 
     // some things are not relevant for memory snapshot
@@ -175,6 +175,7 @@ async function getCacheKey(): Promise<string | null> {
     delete inputs.maxParallelDownloads;
     delete inputs.enableDownloadRetry;
     delete inputs.exitAfterSnapshot;
+    delete inputs.assetUniqueQuery;
 
     inputs.GitHash = GitHash;
     inputs.ProductVersion = ProductVersion;
index 1ae473c..848629b 100644 (file)
@@ -1,54 +1,35 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import BuildConfiguration from "consts:configuration";
 import MonoWasmThreads from "consts:monoWasmThreads";
 import WasmEnableLegacyJsInterop from "consts:WasmEnableLegacyJsInterop";
-import type { MonoConfig } from "./types-api";
-import type { MonoConfigInternal, DotnetModuleInternal } from "./types";
 
-import { mono_assert, CharPtrNull } from "./types";
-import { disableLegacyJsInterop, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, exportedRuntimeAPI, INTERNAL, Module, runtimeHelpers } from "./globals";
+import { DotnetModuleInternal, CharPtrNull } from "./types/internal";
+import { disableLegacyJsInterop, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers } from "./globals";
 import cwraps, { init_c_exports } from "./cwraps";
 import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
 import { toBase64StringImpl } from "./base64";
 import { mono_wasm_init_aot_profiler, mono_wasm_init_browser_profiler } from "./profiler";
-import { mono_on_abort, mono_exit } from "./run";
 import { initialize_marshalers_to_cs } from "./marshal-to-cs";
 import { initialize_marshalers_to_js } from "./marshal-to-js";
 import { init_polyfills_async } from "./polyfills";
 import * as pthreads_worker from "./pthreads/worker";
-import { createPromiseController } from "./promise-controller";
 import { string_decoder } from "./strings";
 import { init_managed_exports } from "./managed-exports";
 import { cwraps_internal } from "./exports-internal";
 import { CharPtr, InstantiateWasmCallBack, InstantiateWasmSuccessCallback } from "./types/emscripten";
-import { instantiate_wasm_asset, mono_download_assets, resolve_asset_path, start_asset_download, wait_for_all_assets } from "./assets";
+import { instantiate_wasm_asset, wait_for_all_assets } from "./assets";
 import { mono_wasm_init_diagnostics } from "./diagnostics";
 import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from "./pthreads/browser";
 import { export_linker } from "./exports-linker";
 import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
 import { getMemorySnapshot, storeMemorySnapshot, getMemorySnapshotSize } from "./snapshot";
-import { loadBootConfig } from "./blazor/_Integration";
 
 // legacy
 import { init_legacy_exports } from "./net6-legacy/corebindings";
 import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy";
 import { BINDING, MONO } from "./net6-legacy/globals";
-import { init_globalization } from "./icu";
-
-let config: MonoConfigInternal = undefined as any;
-let configLoaded = false;
-export const dotnetReady = createPromiseController<any>();
-export const afterConfigLoaded = createPromiseController<MonoConfig>();
-export const memorySnapshotSkippedOrDone = createPromiseController<void>();
-export const afterInstantiateWasm = createPromiseController<void>();
-export const beforePreInit = createPromiseController<void>();
-export const afterPreInit = createPromiseController<void>();
-export const afterPreRun = createPromiseController<void>();
-export const beforeOnRuntimeInitialized = createPromiseController<void>();
-export const afterOnRuntimeInitialized = createPromiseController<void>();
-export const afterPostRun = createPromiseController<void>();
+
 
 // default size if MonoConfig.pthreadPoolSize is undefined
 const MONO_PTHREAD_POOL_SIZE = 4;
@@ -58,14 +39,9 @@ const MONO_PTHREAD_POOL_SIZE = 4;
 export function configureEmscriptenStartup(module: DotnetModuleInternal): void {
     const mark = startMeasure();
 
-    if (!module.configSrc && (!module.config || Object.keys(module.config).length === 0 || !module.config.assets)) {
-        // if config file location nor assets are provided
-        module.configSrc = "./mono-config.json";
-    }
-
-    if (!module["locateFile"]) {
+    if (!module.locateFile) {
         // this is dummy plug so that wasmBinaryFile doesn't try to use URL class
-        module["locateFile"] = module["__locateFile"] = (path) => runtimeHelpers.scriptDirectory + path;
+        module.locateFile = module.__locateFile = (path) => loaderHelpers.scriptDirectory + path;
     }
 
     if (!module.out) {
@@ -75,6 +51,9 @@ export function configureEmscriptenStartup(module: DotnetModuleInternal): void {
     if (!module.err) {
         module.err = console.error.bind(console);
     }
+    loaderHelpers.out = module.out;
+    loaderHelpers.err = module.err;
+    module.mainScriptUrlOrBlob = loaderHelpers.scriptUrl;// this is needed by worker threads
 
     // these all could be overridden on DotnetModuleConfig, we are chaing them to async below, as opposed to emscripten
     // when user set configSrc or config, we are running our default startup sequence.
@@ -86,7 +65,7 @@ export function configureEmscriptenStartup(module: DotnetModuleInternal): void {
     const userOnRuntimeInitialized: () => void = module.onRuntimeInitialized ? module.onRuntimeInitialized : () => { };
 
     // execution order == [0] ==
-    // - default or user Module.instantiateWasm (will start downloading dotnet.wasm)
+    // - default or user Module.instantiateWasm (will start downloading dotnet.native.wasm)
     module.instantiateWasm = (imports, callback) => instantiateWasm(imports, callback, userInstantiateWasm);
     // execution order == [1] ==
     module.preInit = [() => preInit(userPreInit)];
@@ -100,20 +79,23 @@ export function configureEmscriptenStartup(module: DotnetModuleInternal): void {
 
     module.ready.then(async () => {
         // wait for previous stage
-        await afterPostRun.promise;
+        await runtimeHelpers.afterPostRun.promise;
         // startup end
         endMeasure(mark, MeasuredBlock.emscriptenStartup);
         // - here we resolve the promise returned by createDotnetRuntime export
         // - any code after createDotnetRuntime is executed now
-        dotnetReady.promise_control.resolve(exportedRuntimeAPI);
+        runtimeHelpers.dotnetReady.promise_control.resolve(exportedRuntimeAPI);
         runtimeHelpers.runtimeReady = true;
     }).catch(err => {
-        dotnetReady.promise_control.reject(err);
+        runtimeHelpers.dotnetReady.promise_control.reject(err);
     });
-    module.ready = dotnetReady.promise;
+    module.ready = runtimeHelpers.dotnetReady.promise;
     // execution order == [*] ==
     if (!module.onAbort) {
-        module.onAbort = () => mono_on_abort;
+        module.onAbort = (error) => {
+            loaderHelpers.abort_startup(error, false);
+            loaderHelpers.mono_exit(1, error);
+        };
     }
 }
 
@@ -123,19 +105,13 @@ function instantiateWasm(
     userInstantiateWasm?: InstantiateWasmCallBack): any[] {
     // this is called so early that even Module exports like addRunDependency don't exist yet
 
-    if (!Module.configSrc && !Module.config && !userInstantiateWasm) {
-        Module.out("MONO_WASM: configSrc nor config was specified");
-    }
-    normalizeConfig();
-
     const mark = startMeasure();
     if (userInstantiateWasm) {
-        init_globalization();
         // user wasm instantiation doesn't support memory snapshots
-        memorySnapshotSkippedOrDone.promise_control.resolve();
+        runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
         const exports = userInstantiateWasm(imports, (instance: WebAssembly.Instance, module: WebAssembly.Module | undefined) => {
             endMeasure(mark, MeasuredBlock.instantiateWasm);
-            afterInstantiateWasm.promise_control.resolve();
+            runtimeHelpers.afterInstantiateWasm.promise_control.resolve();
             successCallback(instance, module);
         });
         return exports;
@@ -150,9 +126,8 @@ async function instantiateWasmWorker(
     successCallback: InstantiateWasmSuccessCallback
 ): Promise<void> {
     // wait for the config to arrive by message from the main thread
-    await afterConfigLoaded.promise;
+    await loaderHelpers.afterConfigLoaded.promise;
 
-    normalizeConfig();
     replace_linker_placeholders(imports, export_linker());
 
     // Instantiate from the module posted from the main thread.
@@ -168,12 +143,12 @@ function preInit(userPreInit: (() => void)[]) {
     try {
         mono_wasm_pre_init_essential(false);
         if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: preInit");
-        beforePreInit.promise_control.resolve();
+        runtimeHelpers.beforePreInit.promise_control.resolve();
         // all user Module.preInit callbacks
         userPreInit.forEach(fn => fn());
     } catch (err) {
         _print_error("MONO_WASM: user preInint() failed", err);
-        abort_startup(err, true);
+        loaderHelpers.abort_startup(err, true);
         throw err;
     }
     // this will start immediately but return on first await.
@@ -190,11 +165,11 @@ function preInit(userPreInit: (() => void)[]) {
 
             endMeasure(mark, MeasuredBlock.preInit);
         } catch (err) {
-            abort_startup(err, true);
+            loaderHelpers.abort_startup(err, true);
             throw err;
         }
         // signal next stage
-        afterPreInit.promise_control.resolve();
+        runtimeHelpers.afterPreInit.promise_control.resolve();
         Module.removeRunDependency("mono_pre_init");
     })();
 }
@@ -204,14 +179,14 @@ async function preInitWorkerAsync() {
     const mark = startMeasure();
     try {
         if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: preInitWorker");
-        beforePreInit.promise_control.resolve();
+        runtimeHelpers.beforePreInit.promise_control.resolve();
         mono_wasm_pre_init_essential(true);
         await init_polyfills_async();
-        afterPreInit.promise_control.resolve();
+        runtimeHelpers.afterPreInit.promise_control.resolve();
         endMeasure(mark, MeasuredBlock.preInitWorker);
     } catch (err) {
         _print_error("MONO_WASM: user preInitWorker() failed", err);
-        abort_startup(err, true);
+        loaderHelpers.abort_startup(err, true);
         throw err;
     }
 }
@@ -222,19 +197,19 @@ export function preRunWorker() {
         bindings_init();
         endMeasure(mark, MeasuredBlock.preRunWorker);
     } catch (err) {
-        abort_startup(err, true);
+        loaderHelpers.abort_startup(err, true);
         throw err;
     }
     // signal next stage
-    afterPreRun.promise_control.resolve();
+    runtimeHelpers.afterPreRun.promise_control.resolve();
 }
 
 async function preRunAsync(userPreRun: (() => void)[]) {
     Module.addRunDependency("mono_pre_run_async");
     // wait for previous stages
     try {
-        await afterInstantiateWasm.promise;
-        await afterPreInit.promise;
+        await runtimeHelpers.afterInstantiateWasm.promise;
+        await runtimeHelpers.afterPreInit.promise;
         if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: preRunAsync");
         const mark = startMeasure();
         // all user Module.preRun callbacks
@@ -242,46 +217,46 @@ async function preRunAsync(userPreRun: (() => void)[]) {
         endMeasure(mark, MeasuredBlock.preRun);
     } catch (err) {
         _print_error("MONO_WASM: user callback preRun() failed", err);
-        abort_startup(err, true);
+        loaderHelpers.abort_startup(err, true);
         throw err;
     }
     // signal next stage
-    afterPreRun.promise_control.resolve();
+    runtimeHelpers.afterPreRun.promise_control.resolve();
     Module.removeRunDependency("mono_pre_run_async");
 }
 
 async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
     try {
         // wait for previous stage
-        await afterPreRun.promise;
+        await runtimeHelpers.afterPreRun.promise;
         if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: onRuntimeInitialized");
         const mark = startMeasure();
         // signal this stage, this will allow pending assets to allocate memory
-        beforeOnRuntimeInitialized.promise_control.resolve();
+        runtimeHelpers.beforeOnRuntimeInitialized.promise_control.resolve();
 
         await wait_for_all_assets();
 
         // Diagnostics early are not supported with memory snapshot. See below how we enable them later.
         // Please disable startupMemoryCache in order to be able to diagnose or pause runtime startup.
-        if (MonoWasmThreads && !config.startupMemoryCache) {
+        if (MonoWasmThreads && !runtimeHelpers.config.startupMemoryCache) {
             await mono_wasm_init_diagnostics();
         }
 
         // load runtime and apply environment settings (if necessary)
         await mono_wasm_before_memory_snapshot();
 
-        if (config.exitAfterSnapshot) {
+        if (runtimeHelpers.config.exitAfterSnapshot) {
             const reason = runtimeHelpers.ExitStatus
                 ? new runtimeHelpers.ExitStatus(0)
                 : new Error("Snapshot taken, exiting because exitAfterSnapshot was set.");
             reason.silent = true;
 
-            abort_startup(reason, false);
+            loaderHelpers.abort_startup(reason, false);
             return;
         }
 
         if (MonoWasmThreads) {
-            if (config.startupMemoryCache) {
+            if (runtimeHelpers.config.startupMemoryCache) {
                 // we could enable diagnostics after the snapshot is taken
                 await mono_wasm_init_diagnostics();
             }
@@ -296,7 +271,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
             string_decoder.init_fields();
         });
 
-        if (config.startupOptions && INTERNAL.resourceLoader) {
+        if (runtimeHelpers.config.startupOptions && INTERNAL.resourceLoader) {
             if (INTERNAL.resourceLoader.bootConfig.debugBuild && INTERNAL.resourceLoader.bootConfig.cacheBootResources) {
                 INTERNAL.resourceLoader.logToConsole();
             }
@@ -316,17 +291,17 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
         endMeasure(mark, MeasuredBlock.onRuntimeInitialized);
     } catch (err) {
         _print_error("MONO_WASM: onRuntimeInitializedAsync() failed", err);
-        abort_startup(err, true);
+        loaderHelpers.abort_startup(err, true);
         throw err;
     }
     // signal next stage
-    afterOnRuntimeInitialized.promise_control.resolve();
+    runtimeHelpers.afterOnRuntimeInitialized.promise_control.resolve();
 }
 
 async function postRunAsync(userpostRun: (() => void)[]) {
     // wait for previous stage
     try {
-        await afterOnRuntimeInitialized.promise;
+        await runtimeHelpers.afterOnRuntimeInitialized.promise;
         if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: postRunAsync");
         const mark = startMeasure();
 
@@ -339,36 +314,13 @@ async function postRunAsync(userpostRun: (() => void)[]) {
         endMeasure(mark, MeasuredBlock.postRun);
     } catch (err) {
         _print_error("MONO_WASM: user callback posRun() failed", err);
-        abort_startup(err, true);
+        loaderHelpers.abort_startup(err, true);
         throw err;
     }
     // signal next stage
-    afterPostRun.promise_control.resolve();
+    runtimeHelpers.afterPostRun.promise_control.resolve();
 }
 
-// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export function abort_startup(reason: any, should_exit: boolean): void {
-    if (runtimeHelpers.diagnosticTracing) console.trace("MONO_WASM: abort_startup");
-    dotnetReady.promise_control.reject(reason);
-    memorySnapshotSkippedOrDone.promise_control.reject(reason);
-    afterInstantiateWasm.promise_control.reject(reason);
-    beforePreInit.promise_control.reject(reason);
-    afterPreInit.promise_control.reject(reason);
-    afterPreRun.promise_control.reject(reason);
-    beforeOnRuntimeInitialized.promise_control.reject(reason);
-    afterOnRuntimeInitialized.promise_control.reject(reason);
-    afterPostRun.promise_control.reject(reason);
-    if (typeof reason !== "object" || reason.silent !== true) {
-        if (should_exit) {
-            mono_exit(1, reason);
-        }
-        else if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) {
-            const wasm_exit = cwraps.mono_wasm_exit;
-            wasm_exit(1);
-        }
-        throw reason;
-    }
-}
 
 function mono_wasm_pre_init_essential(isWorker: boolean): void {
     if (!isWorker)
@@ -384,7 +336,7 @@ function mono_wasm_pre_init_essential(isWorker: boolean): void {
     }
     // removeRunDependency triggers the dependenciesFulfilled callback (runCaller) in
     // emscripten - on a worker since we don't have any other dependencies that causes run() to get
-    // called too soon; and then it will get called a second time when dotnet.js calls it directly.
+    // called too soon; and then it will get called a second time when dotnet.native.js calls it directly.
     // on a worker run() short-cirtcuits and just calls   readyPromiseResolve, initRuntime and postMessage.
     // sending postMessage twice will break instantiateWasmPThreadWorkerPool on the main thread.
     if (!isWorker)
@@ -396,10 +348,9 @@ async function mono_wasm_pre_init_essential_async(): Promise<void> {
     Module.addRunDependency("mono_wasm_pre_init_essential_async");
 
     await init_polyfills_async();
-    await mono_wasm_load_config(Module.configSrc);
 
     if (MonoWasmThreads) {
-        preAllocatePThreadWorkerPool(MONO_PTHREAD_POOL_SIZE, config);
+        preAllocatePThreadWorkerPool(MONO_PTHREAD_POOL_SIZE, runtimeHelpers.config);
     }
 
     Module.removeRunDependency("mono_wasm_pre_init_essential_async");
@@ -409,7 +360,7 @@ async function mono_wasm_pre_init_full(): Promise<void> {
     if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_pre_init_full");
     Module.addRunDependency("mono_wasm_pre_init_full");
 
-    await mono_download_assets();
+    await loaderHelpers.mono_download_assets();
 
     Module.removeRunDependency("mono_wasm_pre_init_full");
 }
@@ -509,30 +460,24 @@ async function instantiate_wasm_module(
     // this is called so early that even Module exports like addRunDependency don't exist yet
     try {
         let memorySize: number | undefined = undefined;
-        await mono_wasm_load_config(Module.configSrc);
+        await loaderHelpers.afterConfigLoaded;
         if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module");
-        const assetToLoad = resolve_asset_path("dotnetwasm");
-        // FIXME: this would not apply re-try (on connection reset during download) for dotnet.wasm because we could not download the buffer before we pass it to instantiate_wasm_asset
-        const wasmDownloadPromise = start_asset_download(assetToLoad);
-
-        // this is right time as we have free CPU time to do this
-        init_globalization();
 
-        if (config.startupMemoryCache) {
+        if (runtimeHelpers.config.startupMemoryCache) {
             memorySize = await getMemorySnapshotSize();
             runtimeHelpers.loadedMemorySnapshot = !!memorySize;
             runtimeHelpers.storeMemorySnapshotPending = !runtimeHelpers.loadedMemorySnapshot;
         }
         if (!runtimeHelpers.loadedMemorySnapshot) {
             // we should start downloading DLLs etc as they are not in the snapshot
-            memorySnapshotSkippedOrDone.promise_control.resolve();
+            runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
         }
 
-        await wasmDownloadPromise;
-        await beforePreInit.promise;
+        await runtimeHelpers.beforePreInit.promise;
         Module.addRunDependency("instantiate_wasm_module");
 
         replace_linker_placeholders(imports, export_linker());
+        const assetToLoad = await loaderHelpers.wasmDownloadPromise.promise;
         await instantiate_wasm_asset(assetToLoad, imports, successCallback);
         assetToLoad.pendingDownloadInternal = null as any; // GC
         assetToLoad.pendingDownload = null as any; // GC
@@ -552,12 +497,12 @@ async function instantiate_wasm_module(
                 runtimeHelpers.loadedMemorySnapshot = false;
             }
             // now we know if the loading of memory succeeded or not, we can start loading the rest of the assets
-            memorySnapshotSkippedOrDone.promise_control.resolve();
+            runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
         }
-        afterInstantiateWasm.promise_control.resolve();
+        runtimeHelpers.afterInstantiateWasm.promise_control.resolve();
     } catch (err) {
         _print_error("MONO_WASM: instantiate_wasm_module() failed", err);
-        abort_startup(err, true);
+        loaderHelpers.abort_startup(err, true);
         throw err;
     }
     Module.removeRunDependency("instantiate_wasm_module");
@@ -576,30 +521,30 @@ async function mono_wasm_before_memory_snapshot() {
         return;
     }
 
-    for (const k in config.environmentVariables) {
-        const v = config.environmentVariables![k];
+    for (const k in runtimeHelpers.config.environmentVariables) {
+        const v = runtimeHelpers.config.environmentVariables![k];
         if (typeof (v) === "string")
             mono_wasm_setenv(k, v);
         else
             throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`);
     }
-    if (config.startupMemoryCache) {
+    if (runtimeHelpers.config.startupMemoryCache) {
         // disable the trampoline for now, we will re-enable it after we stored the snapshot
         cwraps.mono_jiterp_update_jit_call_dispatcher(0);
     }
-    if (config.runtimeOptions)
-        mono_wasm_set_runtime_options(config.runtimeOptions);
+    if (runtimeHelpers.config.runtimeOptions)
+        mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions);
 
-    if (config.aotProfilerOptions)
-        mono_wasm_init_aot_profiler(config.aotProfilerOptions);
+    if (runtimeHelpers.config.aotProfilerOptions)
+        mono_wasm_init_aot_profiler(runtimeHelpers.config.aotProfilerOptions);
 
-    if (config.browserProfilerOptions)
-        mono_wasm_init_browser_profiler(config.browserProfilerOptions);
+    if (runtimeHelpers.config.browserProfilerOptions)
+        mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions);
 
-    mono_wasm_load_runtime("unused", config.debugLevel);
+    mono_wasm_load_runtime("unused", runtimeHelpers.config.debugLevel);
 
     // we didn't have snapshot yet and the feature is enabled. Take snapshot now.
-    if (config.startupMemoryCache) {
+    if (runtimeHelpers.config.startupMemoryCache) {
         // this would install the mono_jiterp_do_jit_call_indirect
         cwraps.mono_jiterp_update_jit_call_dispatcher(-1);
         await storeMemorySnapshot(Module.HEAP8.buffer);
@@ -615,7 +560,7 @@ export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): vo
         const mark = startMeasure();
         if (debugLevel == undefined) {
             debugLevel = 0;
-            if (config && config.debugLevel) {
+            if (runtimeHelpers.config.debugLevel) {
                 debugLevel = 0 + debugLevel;
             }
         }
@@ -624,7 +569,7 @@ export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): vo
 
     } catch (err: any) {
         _print_error("MONO_WASM: mono_wasm_load_runtime () failed", err);
-        abort_startup(err, false);
+        loaderHelpers.abort_startup(err, false);
     }
 }
 
@@ -650,91 +595,6 @@ export function bindings_init(): void {
     }
 }
 
-/**
- * Loads the mono config file (typically called mono-config.json) asynchroniously
- * Note: the run dependencies are so emsdk actually awaits it in order.
- *
- * @param {string} configFilePath - relative path to the config file
- * @throws Will throw an error if the config file loading fails
- */
-export async function mono_wasm_load_config(configFilePath?: string): Promise<void> {
-    if (configLoaded) {
-        await afterConfigLoaded.promise;
-        return;
-    }
-    configLoaded = true;
-    if (!configFilePath) {
-        normalizeConfig();
-        afterConfigLoaded.promise_control.resolve(config);
-        return;
-    }
-    if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_load_config");
-    try {
-        if (config.startupOptions) {
-            await loadBootConfig(config);
-        } else {
-            const resolveSrc = runtimeHelpers.locateFile(configFilePath);
-            const configResponse = await runtimeHelpers.fetch_like(resolveSrc);
-            const loadedConfig: MonoConfigInternal = (await configResponse.json()) || {};
-            if (loadedConfig.environmentVariables && typeof (loadedConfig.environmentVariables) !== "object")
-                throw new Error("Expected config.environmentVariables to be unset or a dictionary-style object");
-
-            // merge
-            loadedConfig.assets = [...(loadedConfig.assets || []), ...(config.assets || [])];
-            loadedConfig.environmentVariables = { ...(loadedConfig.environmentVariables || {}), ...(config.environmentVariables || {}) };
-            loadedConfig.runtimeOptions = [...(loadedConfig.runtimeOptions || []), ...(config.runtimeOptions || [])];
-            config = runtimeHelpers.config = Module.config = Object.assign(Module.config as any, loadedConfig);
-        }
-
-        normalizeConfig();
-
-        if (Module.onConfigLoaded) {
-            try {
-                await Module.onConfigLoaded(config, exportedRuntimeAPI);
-                normalizeConfig();
-            }
-            catch (err: any) {
-                _print_error("MONO_WASM: onConfigLoaded() failed", err);
-                throw err;
-            }
-        }
-        afterConfigLoaded.promise_control.resolve(config);
-    } catch (err) {
-        const errMessage = `Failed to load config file ${configFilePath} ${err}`;
-        config = runtimeHelpers.config = Module.config = <any>{ message: errMessage, error: err, isError: true };
-        abort_startup(errMessage, true);
-        throw err;
-    }
-}
-
-function normalizeConfig() {
-    // normalize
-    Module.config = config = runtimeHelpers.config = Object.assign(runtimeHelpers.config, Module.config || {});
-
-    config.environmentVariables = config.environmentVariables || {};
-    config.assets = config.assets || [];
-    config.runtimeOptions = config.runtimeOptions || [];
-    config.globalizationMode = config.globalizationMode || "auto";
-
-    if (config.debugLevel === undefined && BuildConfiguration === "Debug") {
-        config.debugLevel = -1;
-    }
-    if (config.diagnosticTracing === undefined && BuildConfiguration === "Debug") {
-        config.diagnosticTracing = true;
-    }
-    runtimeHelpers.diagnosticTracing = !!config.diagnosticTracing;
-    runtimeHelpers.waitForDebugger = config.waitForDebugger;
-    config.startupMemoryCache = !!config.startupMemoryCache;
-    if (config.startupMemoryCache && runtimeHelpers.waitForDebugger) {
-        if (runtimeHelpers.diagnosticTracing) console.info("MONO_WASM: Disabling startupMemoryCache because waitForDebugger is set");
-        config.startupMemoryCache = false;
-    }
-
-    runtimeHelpers.enablePerfMeasure = !!config.browserProfilerOptions
-        && globalThis.performance
-        && typeof globalThis.performance.measure === "function";
-
-}
 
 export function mono_wasm_asm_loaded(assembly_name: CharPtr, assembly_ptr: number, assembly_len: number, pdb_ptr: number, pdb_len: number): void {
     // Only trigger this codepath for assemblies loaded after app is ready
@@ -780,7 +640,6 @@ export function mono_wasm_set_main_args(name: string, allRuntimeArguments: strin
 /// 2. Emscripten does not run any event but preInit in the workers.
 /// 3. At the point when this executes there is no pthread assigned to the worker yet.
 export async function configureWorkerStartup(module: DotnetModuleInternal): Promise<void> {
-    pthreads_worker.setupPreloadChannelToMainThread();
     // This is a good place for subsystems to attach listeners for pthreads_worker.currentWorkerThreadEvents
     pthreads_worker.currentWorkerThreadEvents.addEventListener(pthreads_worker.dotnetPthreadCreated, (ev) => {
         if (runtimeHelpers.diagnosticTracing)
@@ -790,5 +649,5 @@ export async function configureWorkerStartup(module: DotnetModuleInternal): Prom
     // these are the only events which are called on worker
     module.preInit = [() => preInitWorkerAsync()];
     module.instantiateWasm = instantiateWasmWorker;
-    await afterPreInit.promise;
+    await runtimeHelpers.afterPreInit.promise;
 }
index 9ec559f..52f6c8d 100644 (file)
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 import { mono_wasm_new_root_buffer } from "./roots";
-import { MonoString, MonoStringNull, is_nullish, WasmRoot, WasmRootBuffer } from "./types";
+import { MonoString, MonoStringNull, is_nullish, WasmRoot, WasmRootBuffer } from "./types/internal";
 import { Module } from "./globals";
 import cwraps from "./cwraps";
 import { mono_wasm_new_root } from "./roots";
diff --git a/src/mono/wasm/runtime/types/blazor.ts b/src/mono/wasm/runtime/types/blazor.ts
new file mode 100644 (file)
index 0000000..95f88fd
--- /dev/null
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Keep in sync with Microsoft.NET.Sdk.WebAssembly.BootJsonData from the WasmSDK
+export interface BootJsonData {
+    readonly entryAssembly: string;
+    readonly resources: ResourceGroups;
+    /** Gets a value that determines if this boot config was produced from a non-published build (i.e. dotnet build or dotnet run) */
+    readonly debugBuild: boolean;
+    readonly linkerEnabled: boolean;
+    readonly cacheBootResources: boolean;
+    readonly config: string[];
+    readonly icuDataMode: ICUDataMode;
+    readonly startupMemoryCache: boolean | undefined;
+    readonly runtimeOptions: string[] | undefined;
+
+    // These properties are tacked on, and not found in the boot.json file
+    modifiableAssemblies: string | null;
+    aspnetCoreBrowserTools: string | null;
+}
+
+export type BootJsonDataExtension = { [extensionName: string]: ResourceList };
+
+export interface ResourceGroups {
+    readonly assembly: ResourceList;
+    readonly lazyAssembly: ResourceList;
+    readonly pdb?: ResourceList;
+    readonly runtime: ResourceList;
+    readonly satelliteResources?: { [cultureName: string]: ResourceList };
+    readonly libraryInitializers?: ResourceList,
+    readonly extensions?: BootJsonDataExtension
+    readonly runtimeAssets: ExtendedResourceList;
+}
+
+export type ResourceList = { [name: string]: string };
+export type ExtendedResourceList = {
+    [name: string]: {
+        hash: string,
+        behavior: string
+    }
+};
+
+export enum ICUDataMode {
+    Sharded = 0,
+    All = 1,
+    Invariant = 2,
+    Custom = 3
+}
index 11a7e54..a86668b 100644 (file)
@@ -18,4 +18,6 @@ declare module "consts:monoDiagnosticsMock" {
     export default constant;
 }
 
-declare function createEmscripten(module: any): Promise<void>;
+// see src\mono\wasm\runtime\rollup.config.js
+// inline this, because the lambda could allocate closure on hot path otherwise
+declare function mono_assert(condition: unknown, messageFactory: string | (() => string)): asserts condition;
similarity index 80%
rename from src/mono/wasm/runtime/export-types.ts
rename to src/mono/wasm/runtime/types/export-types.ts
index f81661e..379b817 100644 (file)
@@ -1,11 +1,11 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import type { BootJsonData, ICUDataMode } from "./blazor/BootConfig";
-import type { IMemoryView } from "./marshal";
-import type { CreateDotnetRuntimeType, DotnetModuleConfig, RuntimeAPI, MonoConfig, ModuleAPI, AssetEntry, ResourceRequest } from "./types-api";
-import type { EmscriptenModule } from "./types/emscripten";
-import type { dotnet, exit } from "./exports";
+import type { IMemoryView } from "../marshal";
+import type { CreateDotnetRuntimeType, DotnetModuleConfig, RuntimeAPI, MonoConfig, ModuleAPI, AssetEntry, ResourceRequest } from ".";
+import type { EmscriptenModule } from "./emscripten";
+import type { dotnet, exit } from "../loader/index";
+import type { BootJsonData, ICUDataMode } from "./blazor";
 
 // -----------------------------------------------------------
 // this files has all public exports from the dotnet.js module
similarity index 94%
rename from src/mono/wasm/runtime/types-api.ts
rename to src/mono/wasm/runtime/types/index.ts
index 3fd1c32..8698b65 100644 (file)
@@ -1,4 +1,7 @@
-import type { EmscriptenModule, NativePointer } from "./types/emscripten";
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import type { EmscriptenModule, NativePointer } from "./emscripten";
 
 export interface DotnetHostBuilder {
     withConfig(config: MonoConfig): DotnetHostBuilder
@@ -79,7 +82,11 @@ export type MonoConfig = {
     /**
      * application environment
      */
-    applicationEnvironment?: string
+    applicationEnvironment?: string,
+    /**
+     * query string to be used for asset loading
+     */
+    assetUniqueQuery?: string,
 };
 
 export interface ResourceRequest {
@@ -134,6 +141,9 @@ export type AssetBehaviours =
     | "vfs" // load asset into the virtual filesystem (for fopen, File.Open, etc)
     | "dotnetwasm" // the binary of the dotnet runtime
     | "js-module-threads" // the javascript module for threads
+    | "js-module-runtime" // the javascript module for threads
+    | "js-module-dotnet" // the javascript module for threads
+    | "js-module-native" // the javascript module for threads
     | "symbols" // the javascript module for threads
 
 export type GlobalizationMode =
similarity index 74%
rename from src/mono/wasm/runtime/types.ts
rename to src/mono/wasm/runtime/types/internal.ts
index 3f1c811..f9a1623 100644 (file)
@@ -1,8 +1,8 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import { AssetEntry, DotnetModuleConfig, LoadingResource, MonoConfig, RuntimeAPI, WebAssemblyStartOptions } from "./types-api";
-import { CharPtr, EmscriptenModule, ManagedPointer, NativePointer, VoidPtr, Int32Ptr } from "./types/emscripten";
+import type { AssetBehaviours, AssetEntry, DotnetModuleConfig, LoadingResource, MonoConfig, ResourceRequest, RuntimeAPI, WebAssemblyStartOptions } from ".";
+import type { CharPtr, EmscriptenModule, ManagedPointer, NativePointer, VoidPtr, Int32Ptr } from "./emscripten";
 
 export type GCHandle = {
     __brand: "GCHandle"
@@ -92,18 +92,50 @@ export interface AssetEntryInternal extends AssetEntry {
     pendingDownloadInternal?: LoadingResource
 }
 
-export type AssetBehaviours =
-    "resource" // load asset as a managed resource assembly
-    | "assembly" // load asset as a managed assembly
-    | "pdb" // load asset as a managed debugging information
-    | "heap" // store asset into the native heap
-    | "icu" // load asset as an ICU data archive
-    | "vfs" // load asset into the virtual filesystem (for fopen, File.Open, etc)
-    | "dotnetwasm" // the binary of the dotnet runtime
-    | "js-module-threads" // the javascript module for threads
-    | "symbols" // the symbols for the wasm native code
+export type LoaderHelpers = {
+    config: MonoConfigInternal;
+    diagnosticTracing: boolean;
+
+    maxParallelDownloads: number;
+    enableDownloadRetry: boolean;
+
+    loadedFiles: string[],
+    _loaded_files: { url: string, file: string }[];
+    scriptDirectory: string
+    scriptUrl: string
+    assetUniqueQuery?: string
+    preferredIcuAsset: string | null,
+    invariantMode: boolean,
 
+    actual_downloaded_assets_count: number,
+    actual_instantiated_assets_count: number,
+    expected_downloaded_assets_count: number,
+    expected_instantiated_assets_count: number,
+
+    afterConfigLoaded: PromiseAndController<MonoConfig>,
+    allDownloadsQueued: PromiseAndController<void>,
+    wasmDownloadPromise: PromiseAndController<AssetEntryInternal>,
+    runtimeModuleLoaded: PromiseAndController<void>,
+
+    abort_startup: (reason: any, should_exit: boolean) => void,
+    mono_exit: (exit_code: number, reason?: any) => void,
+    createPromiseController: <T>(afterResolve?: () => void, afterReject?: () => void) => PromiseAndController<T>,
+    getPromiseController: <T>(promise: ControllablePromise<T>) => PromiseController<T>,
+    assertIsControllablePromise: <T>(promise: Promise<T>) => asserts promise is ControllablePromise<T>,
+    mono_download_assets: () => Promise<void>,
+    resolve_asset_path: (behavior: AssetBehaviours) => AssetEntryInternal,
+    setup_proxy_console: (id: string, console: Console, origin: string) => void
+    fetch_like: (url: string, init?: RequestInit) => Promise<Response>;
+    locateFile: (path: string, prefix?: string) => string,
+    downloadResource?: (request: ResourceRequest) => LoadingResource | undefined
+    out(message: string): void;
+    err(message: string): void;
+    getApplicationEnvironment?: (bootConfigResponse: Response) => string | null;
+}
 export type RuntimeHelpers = {
+    config: MonoConfigInternal;
+    diagnosticTracing: boolean;
+
     runtime_interop_module: MonoAssembly;
     runtime_interop_namespace: string;
     runtime_interop_exports_classname: string;
@@ -113,29 +145,36 @@ export type RuntimeHelpers = {
     mono_wasm_runtime_is_ready: boolean;
     mono_wasm_bindings_is_ready: boolean;
 
-    loaded_files: string[];
-    maxParallelDownloads: number;
-    enableDownloadRetry: boolean;
-    config: MonoConfigInternal;
-    diagnosticTracing: boolean;
+    loadedMemorySnapshot: boolean,
     enablePerfMeasure: boolean;
     waitForDebugger?: number;
-    fetch_like: (url: string, init?: RequestInit) => Promise<Response>;
-    scriptDirectory: string
-    requirePromise: Promise<Function>
     ExitStatus: ExitStatusError;
     quit: Function,
-    locateFile: (path: string, prefix?: string) => string,
     javaScriptExports: JavaScriptExports,
-    loadedFiles: string[],
-    loadedMemorySnapshot: boolean,
     storeMemorySnapshotPending: boolean,
     memorySnapshotCacheKey: string,
     subtle: SubtleCrypto | null,
-    preferredIcuAsset: string | null,
-    invariantMode: boolean,
     updateMemoryViews: () => void
     runtimeReady: boolean,
+
+    runtimeModuleUrl: string
+    nativeModuleUrl: string
+    allAssetsInMemory: PromiseAndController<void>,
+    dotnetReady: PromiseAndController<any>,
+    memorySnapshotSkippedOrDone: PromiseAndController<void>,
+    afterInstantiateWasm: PromiseAndController<void>,
+    beforePreInit: PromiseAndController<void>,
+    afterPreInit: PromiseAndController<void>,
+    afterPreRun: PromiseAndController<void>,
+    beforeOnRuntimeInitialized: PromiseAndController<void>,
+    afterOnRuntimeInitialized: PromiseAndController<void>,
+    afterPostRun: PromiseAndController<void>,
+
+    //core
+    stringify_as_error_with_stack?: (error: any) => string,
+    instantiate_asset: (asset: AssetEntry, url: string, bytes: Uint8Array) => void,
+    instantiate_symbols_asset: (pendingAsset: AssetEntryInternal) => Promise<void>,
+    jiterpreter_dump_stats?: (x: boolean) => string,
 }
 
 export type AOTProfilerOptions = {
@@ -150,38 +189,6 @@ export type BrowserProfilerOptions = {
 export type DotnetModule = EmscriptenModule & DotnetModuleConfig;
 export type DotnetModuleInternal = EmscriptenModule & DotnetModuleConfig & EmscriptenModuleInternal;
 
-
-export type DotnetModuleConfigImports = {
-    require?: (name: string) => any;
-    fetch?: (url: string, options: any | undefined) => Promise<Response>;
-    fs?: {
-        promises?: {
-            readFile?: (path: string) => Promise<string | Buffer>,
-        }
-        readFileSync?: (path: string, options: any | undefined) => string,
-    };
-    crypto?: {
-        randomBytes?: (size: number) => Buffer
-    };
-    ws?: WebSocket & { Server: any };
-    path?: {
-        normalize?: (path: string) => string,
-        dirname?: (path: string) => string,
-    };
-    url?: any;
-}
-
-// see src\mono\wasm\runtime\rollup.config.js
-// inline this, because the lambda could allocate closure on hot path otherwise
-export function mono_assert(condition: unknown, messageFactory: string | (() => string)): asserts condition {
-    if (!condition) {
-        const message = typeof messageFactory === "string"
-            ? messageFactory
-            : messageFactory();
-        throw new Error(`Assert failed: ${message}`);
-    }
-}
-
 // see src/mono/wasm/driver.c MARSHAL_TYPE_xxx and Runtime.cs MarshalType
 export const enum MarshalType {
     NULL = 0,
@@ -233,30 +240,26 @@ export function is_nullish<T>(value: T | null | undefined): value is null | unde
 }
 
 export type EmscriptenInternals = {
-    isWorker: boolean,
-    isShell: boolean,
     isPThread: boolean,
     disableLegacyJsInterop: boolean,
     quit_: Function,
     ExitStatus: ExitStatusError,
-    requirePromise: Promise<Function>
 };
 export type GlobalObjects = {
     mono: any,
     binding: any,
     internal: any,
     module: DotnetModuleInternal,
-    helpers: RuntimeHelpers,
+    loaderHelpers: LoaderHelpers,
+    runtimeHelpers: RuntimeHelpers,
     api: RuntimeAPI,
 };
 export type EmscriptenReplacements = {
     fetch: any,
     require: any,
-    requirePromise: Promise<Function>,
     updateMemoryViews: Function,
     pthreadReplacements: PThreadReplacements | undefined | null
     scriptDirectory: string;
-    scriptUrl: string
     noExitRuntime?: boolean;
 }
 export interface ExitStatusError {
@@ -401,6 +404,7 @@ export declare interface EmscriptenModuleInternal {
     __locateFile?: (path: string, prefix?: string) => string;
     locateFile?: (path: string, prefix?: string) => string;
     mainScriptUrlOrBlob?: string;
+    ENVIRONMENT_IS_PTHREAD?: boolean;
     wasmModule: WebAssembly.Instance | null;
     ready: Promise<unknown>;
     asm: { memory?: WebAssembly.Memory };
@@ -410,3 +414,45 @@ export declare interface EmscriptenModuleInternal {
     addRunDependency(id: string): void;
     onConfigLoaded?: (config: MonoConfig, api: RuntimeAPI) => void | Promise<void>;
 }
+
+/// A PromiseController encapsulates a Promise together with easy access to its resolve and reject functions.
+/// It's a bit like a TaskCompletionSource in .NET
+export interface PromiseController<T = any> {
+    isDone: boolean;
+    readonly promise: Promise<T>;
+    resolve: (value: T | PromiseLike<T>) => void;
+    reject: (reason?: any) => void;
+}
+
+
+/// A Promise<T> with a controller attached
+export interface ControllablePromise<T = any> extends Promise<T> {
+    __brand: "ControllablePromise"
+}
+
+/// Just a pair of a promise and its controller
+export interface PromiseAndController<T> {
+    promise: ControllablePromise<T>;
+    promise_control: PromiseController<T>;
+}
+
+export type passEmscriptenInternalsType = (internals: EmscriptenInternals) => void;
+export type setGlobalObjectsType = (globalObjects: GlobalObjects) => void;
+export type initializeExportsType = (globalObjects: GlobalObjects) => RuntimeAPI;
+export type initializeReplacementsType = (replacements: EmscriptenReplacements) => void;
+export type configureEmscriptenStartupType = (module: DotnetModuleInternal) => void;
+export type configureWorkerStartupType = (module: DotnetModuleInternal) => Promise<void>
+
+
+export type RuntimeModuleExportsInternal = {
+    setRuntimeGlobals: setGlobalObjectsType,
+    initializeExports: initializeExportsType,
+    initializeReplacements: initializeReplacementsType,
+    configureEmscriptenStartup: configureEmscriptenStartupType,
+    configureWorkerStartup: configureWorkerStartupType,
+    passEmscriptenInternals: passEmscriptenInternalsType,
+}
+
+export type NativeModuleExportsInternal = {
+    default: (unificator: Function) => EmscriptenModuleInternal
+}
\ No newline at end of file
index 9b2e104..e28477b 100644 (file)
@@ -3,11 +3,10 @@
 
 import { prevent_timer_throttling } from "./scheduling";
 import { Queue } from "./queue";
-import { PromiseController, createPromiseController } from "./promise-controller";
-import { mono_assert } from "./types";
-import { Module } from "./globals";
+import { Module, createPromiseController } from "./globals";
 import { setI32 } from "./memory";
 import { VoidPtr } from "./types/emscripten";
+import { PromiseController } from "./types/internal";
 
 const wasm_ws_pending_send_buffer = Symbol.for("wasm ws_pending_send_buffer");
 const wasm_ws_pending_send_buffer_offset = Symbol.for("wasm ws_pending_send_buffer_offset");
index b6347fe..0939b05 100644 (file)
@@ -13,7 +13,7 @@ To add a new web worker, add a definition here and modify the
 
 ## Caveats: a note about pthreads
 
-The workers in this directory are completely standalone from the Emscripten pthreads! they do not have access to the shared instance memory, and do not load the Emscripten `dotnet.js`.  As a result, the workers in this directory cannot use any of pthread APIs or otherwise interact with the runtime in any way, except through message passing, or by having something in the runtime set up their own shared array buffer (which would be inaccessible from wasm).
+The workers in this directory are completely standalone from the Emscripten pthreads! they do not have access to the shared instance memory, and do not load the Emscripten `dotnet.native.js`.  As a result, the workers in this directory cannot use any of pthread APIs or otherwise interact with the runtime in any way, except through message passing, or by having something in the runtime set up their own shared array buffer (which would be inaccessible from wasm).
 
 On the other hand, the workers in this directory also do not depend on a .NET runtime compiled with `-s USE_PTHREADS` and are thus usable on sufficiently new browser using the single-threaded builds of .NET for WebAssembly.
 
index ee29386..668f1ed 100644 (file)
@@ -72,4 +72,4 @@ while (true)
 
 return 0;
 
-static void ShowUsage() => Console.WriteLine($"Usage: symbolicator <path/to/dotnet.js.symbols> [</path/to/patterns-file>] [-|<file-with-traces>]");
+static void ShowUsage() => Console.WriteLine($"Usage: symbolicator <path/to/dotnet.native.js.symbols> [</path/to/patterns-file>] [-|<file-with-traces>]");
index 63dc5d1..90371a4 100644 (file)
@@ -305,6 +305,7 @@ async function dry_run(runArgs) {
             appendElementOnExit: false,
             logExitCode: false,
             pthreadPoolSize: 0,
+            assetUniqueQuery: "?dry_run=true",
             // this just means to not continue startup after the snapshot is taken. 
             // If there was previously a matching snapshot, it will be used.
             exitAfterSnapshot: true
index 5bb5fb3..a63ceb8 100644 (file)
     </ItemGroup>
 
     <Copy SourceFiles="$(NativeBinDir)dotnet.js;
+                       $(NativeBinDir)dotnet.runtime.js;
+                       $(NativeBinDir)dotnet.native.js;
                        $(NativeBinDir)dotnet.d.ts;
                        $(NativeBinDir)dotnet-legacy.d.ts;
                        $(NativeBinDir)package.json;
-                       $(NativeBinDir)dotnet.wasm"
+                       $(NativeBinDir)dotnet.native.wasm"
           DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
           SkipUnchangedFiles="true" />
 
-    <Copy SourceFiles="$(NativeBinDir)dotnet.worker.js"
+    <Copy SourceFiles="$(NativeBinDir)dotnet.native.worker.js"
           DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
-         Condition="Exists('$(NativeBinDir)dotnet.worker.js')"
+         Condition="Exists('$(NativeBinDir)dotnet.native.worker.js')"
           SkipUnchangedFiles="true" />
 
-    <Copy SourceFiles="$(NativeBinDir)dotnet.js.symbols"
+    <Copy SourceFiles="$(NativeBinDir)dotnet.native.js.symbols"
           DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
           SkipUnchangedFiles="true" />
 
index 46bc35c..4172252 100644 (file)
@@ -19,6 +19,13 @@ public class AssetsComputingHelper
         "Microsoft.NETCore.App.Runtime.Mono.perftrace.browser-wasm",
     };
 
+    private static readonly string[] dotnetJsSingleThreadNames = new[]
+    {
+        "dotnet",
+        "dotnet.native",
+        "dotnet.runtime"
+    };
+
     public static bool ShouldFilterCandidate(
         ITaskItem candidate,
         bool timezoneSupport,
@@ -50,7 +57,7 @@ public class AssetsComputingHelper
             ".json" when fromMonoPackage && (fileName == "emcc-props" || fileName == "package") => $"{fileName}{extension} is not used by Blazor",
             ".ts" when fromMonoPackage && fileName == "dotnet.d" => "dotnet type definition is not used by Blazor",
             ".ts" when fromMonoPackage && fileName == "dotnet-legacy.d" => "dotnet type definition is not used by Blazor",
-            ".js" when assetType == "native" && !(fileName == "dotnet" || enableThreads && fileName == "dotnet.worker") => $"{fileName}{extension} is not used by Blazor",
+            ".js" when assetType == "native" && !(dotnetJsSingleThreadNames.Contains(fileName) || enableThreads && fileName == "dotnet.native.worker") => $"{fileName}{extension} is not used by Blazor",
             ".pdb" when !copySymbols => "copying symbols is disabled",
             ".symbols" when fromMonoPackage => "extension .symbols is not required.",
             _ => null
index 97caad5..d8fe399 100644 (file)
@@ -107,11 +107,11 @@ public class ComputeWasmBuildAssets : Task
                 }
 
                 string candidateFileName = candidate.GetMetadata("FileName");
-                if ((candidateFileName == "dotnet" || candidateFileName == "dotnet.worker") && candidate.GetMetadata("Extension") == ".js")
+                if (candidateFileName.StartsWith("dotnet") && candidate.GetMetadata("Extension") == ".js")
                 {
                     string newDotnetJSFileName = null;
                     string newDotNetJSFullPath = null;
-                    if (FingerprintDotNetJs)
+                    if (candidateFileName != "dotnet" || FingerprintDotNetJs)
                     {
                         var itemHash = FileHasher.GetFileHash(candidate.ItemSpec);
                         newDotnetJSFileName = $"{candidateFileName}.{candidate.GetMetadata("NuGetPackageVersion")}.{itemHash}.js";
index 0eddaf4..87467d8 100644 (file)
@@ -165,11 +165,10 @@ public class ComputeWasmPublishAssets : Task
         {
             var key = kvp.Key;
             var asset = kvp.Value;
-            var isDotNetJs = IsDotNetJs(key);
-            var isDotNetWorkerJs = IsDotNetWorkerJs(key);
+            var isDotNetJs = IsAnyDotNetJs(key);
             var isDotNetWasm = IsDotNetWasm(key);
 
-            if (!isDotNetJs && !isDotNetWasm && !isDotNetWorkerJs)
+            if (!isDotNetJs && !isDotNetWasm)
             {
                 if (resolvedNativeAssetToPublish.TryGetValue(Path.GetFileName(asset.GetMetadata("OriginalItemSpec")), out var existing))
                 {
@@ -198,9 +197,17 @@ public class ComputeWasmPublishAssets : Task
                 continue;
             }
 
-            if (isDotNetJs || isDotNetWorkerJs)
+            if (isDotNetJs)
             {
-                var baseName = isDotNetWorkerJs ? "dotnet.worker" : "dotnet";
+                var baseName = Path.GetFileNameWithoutExtension(key);
+                if (baseName.StartsWith("dotnet.native"))
+                    baseName = "dotnet.native";
+                else if (baseName.StartsWith("dotnet.runtime"))
+                    baseName = "dotnet.runtime";
+                else if (baseName.StartsWith("dotnet.worker"))
+                    baseName = "dotnet.worker";
+                else if (baseName.StartsWith("dotnet"))
+                    baseName = "dotnet";
 
                 var aotDotNetJs = WasmAotAssets.SingleOrDefault(a => $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}" == $"{baseName}.js");
                 ITaskItem newDotNetJs = null;
@@ -235,7 +242,10 @@ public class ComputeWasmPublishAssets : Task
 
             if (isDotNetWasm)
             {
-                var aotDotNetWasm = WasmAotAssets.SingleOrDefault(a => $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}" == "dotnet.wasm");
+                var aotDotNetWasm = WasmAotAssets.SingleOrDefault(a => {
+                    var name= $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}";
+                    return name == "dotnet.native.wasm" || name == "dotnet.wasm";
+                });
                 ITaskItem newDotNetWasm = null;
                 if (aotDotNetWasm != null)
                 {
@@ -252,10 +262,14 @@ public class ComputeWasmPublishAssets : Task
 
                 ApplyPublishProperties(newDotNetWasm);
                 nativeStaticWebAssets.Add(newDotNetWasm);
-                if (resolvedNativeAssetToPublish.TryGetValue("dotnet.wasm", out var resolved))
+                if (resolvedNativeAssetToPublish.TryGetValue("dotnet.native.wasm", out var resolved))
                 {
                     filesToRemove.Add(resolved);
                 }
+                else if (resolvedNativeAssetToPublish.TryGetValue("dotnet.wasm", out var resolved2))
+                {
+                    filesToRemove.Add(resolved2);
+                }
                 continue;
             }
         }
@@ -268,19 +282,18 @@ public class ComputeWasmPublishAssets : Task
 
         return nativeStaticWebAssets;
 
-        static bool IsDotNetJs(string key)
+        static bool IsAnyDotNetJs(string key)
         {
             var fileName = Path.GetFileName(key);
-            return fileName.StartsWith("dotnet.", StringComparison.Ordinal) && fileName.EndsWith(".js", StringComparison.Ordinal) && !fileName.Contains("worker");
+            return fileName.StartsWith("dotnet.", StringComparison.Ordinal) && fileName.EndsWith(".js", StringComparison.Ordinal);
         }
 
-        static bool IsDotNetWorkerJs(string key)
+        static bool IsDotNetWasm(string key)
         {
-            var fileName = Path.GetFileName(key);
-            return fileName.StartsWith("dotnet.worker.", StringComparison.Ordinal) && fileName.EndsWith(".js", StringComparison.Ordinal);
+            var name = Path.GetFileName(key);
+            return string.Equals("dotnet.native.wasm", name, StringComparison.Ordinal)
+                || string.Equals("dotnet.wasm", name, StringComparison.Ordinal);
         }
-
-        static bool IsDotNetWasm(string key) => string.Equals("dotnet.wasm", Path.GetFileName(key), StringComparison.Ordinal);
     }
 
     private List<ITaskItem> ProcessSymbolAssets(
index 31ed093..0e8dc24 100644 (file)
@@ -196,8 +196,7 @@ public class GenerateWasmBootJson : Task
                         string.Equals(assetTraitValue, "native", StringComparison.OrdinalIgnoreCase))
                 {
                     Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as a native application resource.", resource.ItemSpec);
-                    if (string.Equals(fileName, "dotnet", StringComparison.OrdinalIgnoreCase) &&
-                        string.Equals(fileExtension, ".wasm", StringComparison.OrdinalIgnoreCase))
+                    if (fileName.StartsWith("dotnet", StringComparison.OrdinalIgnoreCase) && string.Equals(fileExtension, ".wasm", StringComparison.OrdinalIgnoreCase))
                     {
                         behavior = "dotnetwasm";
                     }
@@ -286,7 +285,7 @@ public class GenerateWasmBootJson : Task
                 UseSimpleDictionaryFormat = true
             });
 
-            result.extensions = new Dictionary<string, Dictionary<string, object>> ();
+            result.extensions = new Dictionary<string, Dictionary<string, object>>();
             foreach (var configExtension in Extensions)
             {
                 var key = configExtension.GetMetadata("key");
index 439ebf6..7370e93 100644 (file)
@@ -78,6 +78,21 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask
         public WasmEntry(string name, string hash) : base(name, hash, "dotnetwasm") { }
     }
 
+    private sealed class LoaderJsEntry : AssetEntry
+    {
+        public LoaderJsEntry(string name, string hash) : base(name, hash, "js-module-dotnet") { }
+    }
+
+    private sealed class NativeJsEntry : AssetEntry
+    {
+        public NativeJsEntry(string name, string hash) : base(name, hash, "js-module-native") { }
+    }
+
+    private sealed class RuntimeJsEntry : AssetEntry
+    {
+        public RuntimeJsEntry(string name, string hash) : base(name, hash, "js-module-runtime") { }
+    }
+
     private sealed class ThreadsWorkerEntry : AssetEntry
     {
         public ThreadsWorkerEntry(string name, string hash) : base(name, hash, "js-module-threads") { }
@@ -198,15 +213,27 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask
             var dest = Path.Combine(AppDir!, name);
             if (!FileCopyChecked(item.ItemSpec, dest, "NativeAssets"))
                 return false;
-            if (name == "dotnet.wasm")
+            if (name == "dotnet.js")
+            {
+                config.Assets.Add(new LoaderJsEntry (name, Utils.ComputeIntegrity(item.ItemSpec)) );
+            }
+            else if (name == "dotnet.native.wasm")
             {
                 config.Assets.Add(new WasmEntry (name, Utils.ComputeIntegrity(item.ItemSpec)) );
             }
-            else if (IncludeThreadsWorker && name == "dotnet.worker.js")
+            else if (name == "dotnet.native.js")
+            {
+                config.Assets.Add(new NativeJsEntry (name, Utils.ComputeIntegrity(item.ItemSpec)) );
+            }
+            else if (name == "dotnet.runtime.js")
+            {
+                config.Assets.Add(new RuntimeJsEntry (name, Utils.ComputeIntegrity(item.ItemSpec)) );
+            }
+            else if (IncludeThreadsWorker && name == "dotnet.native.worker.js")
             {
                 config.Assets.Add(new ThreadsWorkerEntry (name, Utils.ComputeIntegrity(item.ItemSpec)));
             }
-            else if(name == "dotnet.js.symbols")
+            else if(name == "dotnet.native.js.symbols")
             {
                 config.Assets.Add(new SymbolsData(name, Utils.ComputeIntegrity(item.ItemSpec)));
             }
index a552adf..b5a6539 100644 (file)
@@ -22,7 +22,7 @@ public abstract class WasmAppBuilderBaseTask : Task
     [Required]
     public string[] Assemblies { get; set; } = Array.Empty<string>();
 
-    // files like dotnet.wasm, icudt.dat etc
+    // files like dotnet.native.wasm, icudt.dat etc
     [NotNull]
     [Required]
     public ITaskItem[] NativeAssets { get; set; } = Array.Empty<ITaskItem>();