diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets
index d474e19b69acc..7ff8fccc2c017 100644
--- a/eng/testing/linker/trimmingTests.targets
+++ b/eng/testing/linker/trimmingTests.targets
@@ -165,7 +165,7 @@
-
+
diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml
index d1f5af506a247..5561632ceaaaa 100644
--- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml
+++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml
@@ -11,6 +11,7 @@
to be trimmed when Invariant=true, and allows for the Settings static cctor (on Unix) to be preserved when Invariant=false. -->
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
index 37d32f9cd57f4..32d284e85989c 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
@@ -628,7 +628,7 @@ private static CultureData CreateCultureWithInvariantData()
// all available calendar type(s). The first one is the default calendar
invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN };
- if (!GlobalizationMode.Invariant)
+ if (!GlobalizationMode.InvariantNoLoad)
{
// Store for specific data about each calendar
invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS];
@@ -646,7 +646,7 @@ private static CultureData CreateCultureWithInvariantData()
invariant._iDefaultMacCodePage = 10000; // default macintosh code page
invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page
- if (GlobalizationMode.Invariant)
+ if (GlobalizationMode.InvariantNoLoad)
{
invariant._sLocalizedCountry = invariant._sNativeCountry;
}
@@ -2228,7 +2228,7 @@ private string[] GetNativeDigits()
internal void GetNFIValues(NumberFormatInfo nfi)
{
- if (GlobalizationMode.Invariant || IsInvariantCulture)
+ if (GlobalizationMode.InvariantNoLoad || IsInvariantCulture)
{
nfi._positiveSign = _sPositiveSign!;
nfi._negativeSign = _sNegativeSign!;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs
index 314bb33692dc7..78cebb45b9a00 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs
@@ -25,6 +25,21 @@ private static partial class Settings
// This allows for the whole Settings nested class to be trimmed when Invariant=true, and allows for the Settings
// static cctor (on Unix) to be preserved when Invariant=false.
internal static bool Invariant => Settings.Invariant;
+
+ // same as GlobalizationMode.Invariant but doesn't trigger ICU load in GlobalizationMode.Settings.cctor
+ // during runtime startup on Browser platform
+ internal static bool InvariantNoLoad
+ {
+ get
+ {
+#if TARGET_BROWSER
+ return AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
+#else
+ return Settings.Invariant;
+#endif
+ }
+ }
+
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS || TARGET_BROWSER
internal static bool Hybrid => Settings.Hybrid;
#endif
diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets
index d75513604304c..c92fd9d6e185d 100644
--- a/src/mono/browser/build/BrowserWasmApp.targets
+++ b/src/mono/browser/build/BrowserWasmApp.targets
@@ -156,6 +156,8 @@
RuntimeAssetsLocation="$(WasmRuntimeAssetsLocation)"
CacheBootResources="$(BlazorCacheBootResources)"
RuntimeConfigJsonPath="$(_WasmRuntimeConfigFilePath)"
+ IsAot="$(RunAOTCompilation)"
+ IsMultiThreaded="$(WasmEnableThreads)"
>
diff --git a/src/mono/browser/runtime/assets.ts b/src/mono/browser/runtime/assets.ts
index 5c3f7ecce8822..f8725a36e1250 100644
--- a/src/mono/browser/runtime/assets.ts
+++ b/src/mono/browser/runtime/assets.ts
@@ -52,6 +52,9 @@ export function instantiate_asset (asset: AssetEntry, url: string, bytes: Uint8A
if (fileName.startsWith("/"))
fileName = fileName.substring(1);
if (parentDirectory) {
+ if (!parentDirectory.startsWith("/"))
+ parentDirectory = "/" + parentDirectory;
+
mono_log_debug(`Creating directory '${parentDirectory}'`);
Module.FS_createPath(
@@ -85,8 +88,7 @@ export function instantiate_asset (asset: AssetEntry, url: string, bytes: Uint8A
} else if (asset.behavior === "pdb") {
cwraps.mono_wasm_add_assembly(virtualName, offset!, bytes.length);
} else if (asset.behavior === "icu") {
- if (!mono_wasm_load_icu_data(offset!))
- Module.err(`Error loading ICU asset ${asset.name}`);
+ mono_wasm_load_icu_data(offset!);
} else if (asset.behavior === "resource") {
cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture || "", offset!, bytes.length);
}
diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts
index 510aa61fcc91c..aa38e6ef9916d 100644
--- a/src/mono/browser/runtime/dotnet.d.ts
+++ b/src/mono/browser/runtime/dotnet.d.ts
@@ -243,8 +243,10 @@ type ResourceExtensions = {
};
interface ResourceGroups {
hash?: string;
+ coreAssembly?: ResourceList;
assembly?: ResourceList;
lazyAssembly?: ResourceList;
+ corePdb?: ResourceList;
pdb?: ResourceList;
jsModuleWorker?: ResourceList;
jsModuleNative: ResourceList;
@@ -258,6 +260,9 @@ interface ResourceGroups {
modulesAfterConfigLoaded?: ResourceList;
modulesAfterRuntimeReady?: ResourceList;
extensions?: ResourceExtensions;
+ coreVfs?: {
+ [virtualPath: string]: ResourceList;
+ };
vfs?: {
[virtualPath: string]: ResourceList;
};
diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts
index 047fcb60c9502..e1eeed5ddce1d 100644
--- a/src/mono/browser/runtime/globals.ts
+++ b/src/mono/browser/runtime/globals.ts
@@ -57,6 +57,7 @@ export function setRuntimeGlobals (globalObjects: GlobalObjects) {
const rh: Partial = {
gitHash,
+ coreAssetsInMemory: createPromiseController(),
allAssetsInMemory: createPromiseController(),
dotnetReady: createPromiseController(),
afterInstantiateWasm: createPromiseController(),
@@ -64,7 +65,8 @@ export function setRuntimeGlobals (globalObjects: GlobalObjects) {
afterPreInit: createPromiseController(),
afterPreRun: createPromiseController(),
beforeOnRuntimeInitialized: createPromiseController(),
- afterMonoStarted: createPromiseController(),
+ afterMonoStarted: createPromiseController(),
+ afterDeputyReady: createPromiseController(),
afterIOStarted: createPromiseController(),
afterOnRuntimeInitialized: createPromiseController(),
afterPostRun: createPromiseController(),
diff --git a/src/mono/browser/runtime/icu.ts b/src/mono/browser/runtime/icu.ts
index 16b23871f9871..b6c789f1138f6 100644
--- a/src/mono/browser/runtime/icu.ts
+++ b/src/mono/browser/runtime/icu.ts
@@ -4,8 +4,8 @@
import cwraps from "./cwraps";
import { VoidPtr } from "./types/emscripten";
-// @offset must be the address of an ICU data archive in the native heap.
-// returns true on success.
-export function mono_wasm_load_icu_data (offset: VoidPtr): boolean {
- return (cwraps.mono_wasm_load_icu_data(offset)) === 1;
+export function mono_wasm_load_icu_data (offset: VoidPtr) {
+ if (!cwraps.mono_wasm_load_icu_data(offset)) {
+ throw new Error("Failed to load ICU data");
+ }
}
diff --git a/src/mono/browser/runtime/loader/assets.ts b/src/mono/browser/runtime/loader/assets.ts
index ad9d3650791e4..926042d57e7dd 100644
--- a/src/mono/browser/runtime/loader/assets.ts
+++ b/src/mono/browser/runtime/loader/assets.ts
@@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads";
import { PThreadPtrNull, type AssetEntryInternal, type PThreadWorker, type PromiseAndController } from "../types/internal";
import type { AssetBehaviors, AssetEntry, LoadingResource, ResourceList, SingleAssetBehaviors as SingleAssetBehaviors, WebAssemblyBootResourceType } from "../types";
-import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
+import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, ENVIRONMENT_IS_WORKER, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { createPromiseController } from "./promise-controller";
import { mono_log_debug, mono_log_warn } from "./logging";
import { mono_exit } from "./exit";
@@ -18,6 +18,7 @@ import { mono_log_info } from "./logging";
let throttlingPromise: PromiseAndController | undefined;
// in order to prevent net::ERR_INSUFFICIENT_RESOURCES if we start downloading too many files at same time
let parallel_count = 0;
+const coreAssetsToLoad: AssetEntryInternal[] = [];
const assetsToLoad: AssetEntryInternal[] = [];
const singleAssets: Map = new Map();
@@ -152,21 +153,25 @@ export function resolve_single_asset_path (behavior: SingleAssetBehaviors): Asse
export async function mono_download_assets (): Promise {
mono_log_debug("mono_download_assets");
try {
- const promises_of_assets: Promise[] = [];
+ const promises_of_assets_core: Promise[] = [];
+ const promises_of_assets_remaining: Promise[] = [];
- const countAndStartDownload = (asset: AssetEntryInternal) => {
+ const countAndStartDownload = (asset: AssetEntryInternal, promises_list: Promise[]) => {
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));
+ promises_list.push(start_asset_download(asset));
}
};
// start fetching assets in parallel
+ for (const asset of coreAssetsToLoad) {
+ countAndStartDownload(asset, promises_of_assets_core);
+ }
for (const asset of assetsToLoad) {
- countAndStartDownload(asset);
+ countAndStartDownload(asset, promises_of_assets_remaining);
}
loaderHelpers.allDownloadsQueued.promise_control.resolve();
@@ -174,55 +179,73 @@ export async function mono_download_assets (): Promise {
// continue after the dotnet.runtime.js was loaded
await loaderHelpers.runtimeModuleLoaded.promise;
- const promises_of_asset_instantiation: Promise[] = [];
- for (const downloadPromise of promises_of_assets) {
- promises_of_asset_instantiation.push((async () => {
- const asset = await downloadPromise;
- if (asset.buffer) {
- if (!skipInstantiateByAssetTypes[asset.behavior]) {
- mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array-like or buffer-like or promise of these");
- mono_assert(typeof asset.resolvedUrl === "string", "resolvedUrl must be string");
- const url = asset.resolvedUrl!;
- const buffer = await asset.buffer;
- const data = new Uint8Array(buffer);
- cleanupAsset(asset);
-
- // wait till after onRuntimeInitialized
-
- await runtimeHelpers.beforeOnRuntimeInitialized.promise;
- runtimeHelpers.instantiate_asset(asset, url, data);
+ const instantiate = async (downloadPromise: Promise) => {
+ const asset = await downloadPromise;
+ if (asset.buffer) {
+ if (!skipInstantiateByAssetTypes[asset.behavior]) {
+ mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array-like or buffer-like or promise of these");
+ mono_assert(typeof asset.resolvedUrl === "string", "resolvedUrl must be string");
+ const url = asset.resolvedUrl!;
+ const buffer = await asset.buffer;
+ const data = new Uint8Array(buffer);
+ cleanupAsset(asset);
+
+ // wait till after onRuntimeInitialized
+
+ await runtimeHelpers.beforeOnRuntimeInitialized.promise;
+ runtimeHelpers.instantiate_asset(asset, url, data);
+ }
+ } 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 {
- 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 (asset.behavior === "symbols") {
- await runtimeHelpers.instantiate_symbols_asset(asset);
- cleanupAsset(asset);
- } else if (asset.behavior === "segmentation-rules") {
- await runtimeHelpers.instantiate_segmentation_rules_asset(asset);
- cleanupAsset(asset);
- }
-
- if (skipBufferByAssetTypes[asset.behavior]) {
- ++loaderHelpers.actual_downloaded_assets_count;
- }
+ if (asset.behavior === "symbols") {
+ await runtimeHelpers.instantiate_symbols_asset(asset);
+ cleanupAsset(asset);
+ } else if (asset.behavior === "segmentation-rules") {
+ await runtimeHelpers.instantiate_segmentation_rules_asset(asset);
+ cleanupAsset(asset);
+ }
+
+ if (skipBufferByAssetTypes[asset.behavior]) {
+ ++loaderHelpers.actual_downloaded_assets_count;
}
}
- })());
+ }
+ };
+
+ const promises_of_asset_instantiation_core: Promise[] = [];
+ const promises_of_asset_instantiation_remaining: Promise[] = [];
+ for (const downloadPromise of promises_of_assets_core) {
+ promises_of_asset_instantiation_core.push(instantiate(downloadPromise));
+ }
+ for (const downloadPromise of promises_of_assets_remaining) {
+ promises_of_asset_instantiation_remaining.push(instantiate(downloadPromise));
}
// 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();
+ // and we are not awaiting it here
+ Promise.all(promises_of_asset_instantiation_core).then(() => {
+ if (!ENVIRONMENT_IS_WORKER) {
+ runtimeHelpers.coreAssetsInMemory.promise_control.resolve();
+ }
+ }).catch(err => {
+ loaderHelpers.err("Error in mono_download_assets: " + err);
+ mono_exit(1, err);
+ throw err;
+ });
+ Promise.all(promises_of_asset_instantiation_remaining).then(async () => {
+ if (!ENVIRONMENT_IS_WORKER) {
+ await runtimeHelpers.coreAssetsInMemory.promise;
+ runtimeHelpers.allAssetsInMemory.promise_control.resolve();
+ }
}).catch(err => {
loaderHelpers.err("Error in mono_download_assets: " + err);
mono_exit(1, err);
@@ -251,7 +274,11 @@ export function prepareAssets () {
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");
- assetsToLoad.push(asset);
+ if (asset.isCore) {
+ coreAssetsToLoad.push(asset);
+ } else {
+ assetsToLoad.push(asset);
+ }
set_single_asset(asset);
}
} else if (config.resources) {
@@ -268,23 +295,55 @@ export function prepareAssets () {
convert_single_asset(modulesAssets, resources.jsModuleWorker, "js-module-threads");
}
+ const addAsset = (asset: AssetEntryInternal, isCore: boolean) => {
+ if (isCore) {
+ asset.isCore = true;
+ coreAssetsToLoad.push(asset);
+ } else {
+ assetsToLoad.push(asset);
+ }
+ };
+
+ if (resources.coreAssembly) {
+ for (const name in resources.coreAssembly) {
+ addAsset({
+ name,
+ hash: resources.coreAssembly[name],
+ behavior: "assembly"
+ }, true);
+ }
+ }
+
if (resources.assembly) {
for (const name in resources.assembly) {
- assetsToLoad.push({
+ addAsset({
name,
hash: resources.assembly[name],
behavior: "assembly"
- });
+ }, !resources.coreAssembly); // if there are no core assemblies, then all assemblies are core
}
}
- if (config.debugLevel != 0 && loaderHelpers.isDebuggingSupported() && resources.pdb) {
- for (const name in resources.pdb) {
- assetsToLoad.push({
- name,
- hash: resources.pdb[name],
- behavior: "pdb"
- });
+
+ if (config.debugLevel != 0 && loaderHelpers.isDebuggingSupported()) {
+ if (resources.corePdb) {
+ for (const name in resources.corePdb) {
+ addAsset({
+ name,
+ hash: resources.corePdb[name],
+ behavior: "pdb"
+ }, true);
+ }
+ }
+
+ if (resources.pdb) {
+ for (const name in resources.pdb) {
+ addAsset({
+ name,
+ hash: resources.pdb[name],
+ behavior: "pdb"
+ }, !resources.corePdb); // if there are no core pdbs, then all pdbs are core
+ }
}
}
@@ -301,15 +360,28 @@ export function prepareAssets () {
}
}
+ if (resources.coreVfs) {
+ for (const virtualPath in resources.coreVfs) {
+ for (const name in resources.coreVfs[virtualPath]) {
+ addAsset({
+ name,
+ hash: resources.coreVfs[virtualPath][name],
+ behavior: "vfs",
+ virtualPath
+ }, true);
+ }
+ }
+ }
+
if (resources.vfs) {
for (const virtualPath in resources.vfs) {
for (const name in resources.vfs[virtualPath]) {
- assetsToLoad.push({
+ addAsset({
name,
hash: resources.vfs[virtualPath][name],
behavior: "vfs",
virtualPath
- });
+ }, !resources.coreVfs);
}
}
}
@@ -336,7 +408,7 @@ export function prepareAssets () {
if (resources.wasmSymbols) {
for (const name in resources.wasmSymbols) {
- assetsToLoad.push({
+ coreAssetsToLoad.push({
name,
hash: resources.wasmSymbols[name],
behavior: "symbols"
@@ -363,7 +435,7 @@ export function prepareAssets () {
}
}
- config.assets = [...assetsToLoad, ...modulesAssets];
+ config.assets = [...coreAssetsToLoad, ...assetsToLoad, ...modulesAssets];
}
export function prepareAssetsWorker () {
diff --git a/src/mono/browser/runtime/pthreads/deputy-thread.ts b/src/mono/browser/runtime/pthreads/deputy-thread.ts
index 4b514b28a4aed..1e2041fa44dae 100644
--- a/src/mono/browser/runtime/pthreads/deputy-thread.ts
+++ b/src/mono/browser/runtime/pthreads/deputy-thread.ts
@@ -8,8 +8,10 @@ import { mono_log_error, mono_log_info } from "../logging";
import { monoThreadInfo, postMessageToMain, update_thread_info } from "./shared";
import { Module, loaderHelpers, runtimeHelpers } from "../globals";
import { start_runtime } from "../startup";
-import { WorkerToMainMessageType } from "../types/internal";
+import { MainToWorkerMessageType, WorkerToMainMessageType } from "../types/internal";
import { forceThreadMemoryViewRefresh } from "../memory";
+import { install_main_synchronization_context } from "../managed-exports";
+import { pthread_self } from "./worker-thread";
export function mono_wasm_start_deputy_thread_async () {
if (!WasmEnableThreads) return;
@@ -31,11 +33,26 @@ export function mono_wasm_start_deputy_thread_async () {
try {
forceThreadMemoryViewRefresh();
+ pthread_self.addEventListenerFromBrowser((message) => {
+ if (message.data.cmd == MainToWorkerMessageType.allAssetsLoaded) {
+ runtimeHelpers.allAssetsInMemory.promise_control.resolve();
+ }
+ });
+
await start_runtime();
postMessageToMain({
monoCmd: WorkerToMainMessageType.deputyStarted,
info: monoThreadInfo,
+ });
+
+ await runtimeHelpers.allAssetsInMemory.promise;
+
+ runtimeHelpers.proxyGCHandle = install_main_synchronization_context(runtimeHelpers.config.jsThreadBlockingMode!);
+
+ postMessageToMain({
+ monoCmd: WorkerToMainMessageType.deputyReady,
+ info: monoThreadInfo,
deputyProxyGCHandle: runtimeHelpers.proxyGCHandle,
});
} catch (err) {
diff --git a/src/mono/browser/runtime/pthreads/ui-thread.ts b/src/mono/browser/runtime/pthreads/ui-thread.ts
index 0dc2b80c57e0f..94016a529424c 100644
--- a/src/mono/browser/runtime/pthreads/ui-thread.ts
+++ b/src/mono/browser/runtime/pthreads/ui-thread.ts
@@ -96,7 +96,11 @@ function monoWorkerMessageHandler (worker: PThreadWorker, ev: MessageEvent)
worker.info = Object.assign(worker.info!, message.info, {});
break;
case WorkerToMainMessageType.deputyStarted:
- runtimeHelpers.afterMonoStarted.promise_control.resolve(message.deputyProxyGCHandle);
+ runtimeHelpers.deputyWorker = worker;
+ runtimeHelpers.afterMonoStarted.promise_control.resolve();
+ break;
+ case WorkerToMainMessageType.deputyReady:
+ runtimeHelpers.afterDeputyReady.promise_control.resolve(message.deputyProxyGCHandle);
break;
case WorkerToMainMessageType.ioStarted:
runtimeHelpers.afterIOStarted.promise_control.resolve();
diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts
index b89522caff198..4d07e712358b0 100644
--- a/src/mono/browser/runtime/startup.ts
+++ b/src/mono/browser/runtime/startup.ts
@@ -4,7 +4,7 @@
import WasmEnableThreads from "consts:wasmEnableThreads";
import BuildConfiguration from "consts:configuration";
-import { DotnetModuleInternal, CharPtrNull } from "./types/internal";
+import { DotnetModuleInternal, CharPtrNull, MainToWorkerMessageType } from "./types/internal";
import { exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, createPromiseController, mono_assert } from "./globals";
import cwraps, { init_c_exports, threads_c_functions as tcwraps } from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
@@ -14,7 +14,7 @@ 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 { strings_init, utf8ToString } from "./strings";
-import { init_managed_exports, install_main_synchronization_context } from "./managed-exports";
+import { init_managed_exports } from "./managed-exports";
import { cwraps_internal } from "./exports-internal";
import { CharPtr, InstantiateWasmCallBack, InstantiateWasmSuccessCallback } from "./types/emscripten";
import { wait_for_all_assets } from "./assets";
@@ -255,16 +255,21 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: () => void)
threadsReady = mono_wasm_init_threads();
}
- await wait_for_all_assets();
+ await runtimeHelpers.coreAssetsInMemory.promise;
if (runtimeHelpers.config.virtualWorkingDirectory) {
const FS = Module.FS;
const cwd = runtimeHelpers.config.virtualWorkingDirectory;
- const wds = FS.stat(cwd);
- if (!wds) {
+ try {
+ const wds = FS.stat(cwd);
+ if (!wds) {
+ Module.FS_createPath("/", cwd, true, true);
+ } else {
+ mono_assert(wds && FS.isDir(wds.mode), () => `FS.chdir: ${cwd} is not a directory`);
+ }
+ } catch (e) {
Module.FS_createPath("/", cwd, true, true);
}
- mono_assert(wds && FS.isDir(wds.mode), () => `FS.chdir: ${cwd} is not a directory`);
FS.chdir(cwd);
}
@@ -287,7 +292,7 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: () => void)
runtimeHelpers.managedThreadTID = tcwraps.mono_wasm_create_deputy_thread();
// await mono started on deputy thread
- runtimeHelpers.proxyGCHandle = await runtimeHelpers.afterMonoStarted.promise;
+ await runtimeHelpers.afterMonoStarted.promise;
runtimeHelpers.ioThreadTID = tcwraps.mono_wasm_create_io_thread();
// TODO make UI thread not managed/attached https://github.com/dotnet/runtime/issues/100411
@@ -311,6 +316,16 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: () => void)
await runtimeHelpers.afterIOStarted.promise;
}
+ await wait_for_all_assets();
+
+ if (WasmEnableThreads) {
+ runtimeHelpers.deputyWorker.thread!.postMessageToWorker({
+ type:"deputyThread",
+ cmd: MainToWorkerMessageType.allAssetsLoaded,
+ });
+ runtimeHelpers.proxyGCHandle = await runtimeHelpers.afterDeputyReady.promise;
+ }
+
runtimeList.registerRuntime(exportedRuntimeAPI);
if (!runtimeHelpers.mono_wasm_runtime_is_ready) mono_wasm_runtime_ready();
@@ -545,12 +560,11 @@ export async function start_runtime () {
monoThreadInfo.isRegistered = true;
runtimeHelpers.currentThreadTID = monoThreadInfo.pthreadId = runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr();
update_thread_info();
- runtimeHelpers.proxyGCHandle = install_main_synchronization_context(runtimeHelpers.config.jsThreadBlockingMode!);
runtimeHelpers.isManagedRunningOnCurrentThread = true;
}
// get GCHandle of the ctx
- runtimeHelpers.afterMonoStarted.promise_control.resolve(runtimeHelpers.proxyGCHandle);
+ runtimeHelpers.afterMonoStarted.promise_control.resolve();
if (runtimeHelpers.config.interpreterPgo) {
await interp_pgo_load_data();
diff --git a/src/mono/browser/runtime/types/index.ts b/src/mono/browser/runtime/types/index.ts
index dad313a47a07e..df2fb6b69c81b 100644
--- a/src/mono/browser/runtime/types/index.ts
+++ b/src/mono/browser/runtime/types/index.ts
@@ -199,8 +199,10 @@ export type ResourceExtensions = { [extensionName: string]: ResourceList };
export interface ResourceGroups {
hash?: string;
+ coreAssembly?: ResourceList; // nullable only temporarily
assembly?: ResourceList; // nullable only temporarily
lazyAssembly?: ResourceList; // nullable only temporarily
+ corePdb?: ResourceList;
pdb?: ResourceList;
jsModuleWorker?: ResourceList;
@@ -216,6 +218,7 @@ export interface ResourceGroups {
modulesAfterRuntimeReady?: ResourceList
extensions?: ResourceExtensions
+ coreVfs?: { [virtualPath: string]: ResourceList };
vfs?: { [virtualPath: string]: ResourceList };
}
diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts
index 3e83f751c0b8a..6b9f16cafa05f 100644
--- a/src/mono/browser/runtime/types/internal.ts
+++ b/src/mono/browser/runtime/types/internal.ts
@@ -111,6 +111,7 @@ export interface AssetEntryInternal extends AssetEntry {
pendingDownloadInternal?: LoadingResource
noCache?: boolean
useCredentials?: boolean
+ isCore?: boolean
}
export type LoaderHelpers = {
@@ -209,11 +210,13 @@ export type RuntimeHelpers = {
proxyGCHandle: GCHandle | undefined,
managedThreadTID: PThreadPtr,
ioThreadTID: PThreadPtr,
+ deputyWorker: PThreadWorker,
currentThreadTID: PThreadPtr,
isManagedRunningOnCurrentThread: boolean,
isPendingSynchronousCall: boolean, // true when we are in the middle of a synchronous call from managed code from same thread
cspPolicy: boolean,
+ coreAssetsInMemory: PromiseAndController,
allAssetsInMemory: PromiseAndController,
dotnetReady: PromiseAndController,
afterInstantiateWasm: PromiseAndController,
@@ -221,7 +224,8 @@ export type RuntimeHelpers = {
afterPreInit: PromiseAndController,
afterPreRun: PromiseAndController,
beforeOnRuntimeInitialized: PromiseAndController,
- afterMonoStarted: PromiseAndController,
+ afterMonoStarted: PromiseAndController,
+ afterDeputyReady: PromiseAndController,
afterIOStarted: PromiseAndController,
afterOnRuntimeInitialized: PromiseAndController,
afterPostRun: PromiseAndController,
@@ -501,12 +505,14 @@ export const enum WorkerToMainMessageType {
deputyCreated = "createdDeputy",
deputyFailed = "deputyFailed",
deputyStarted = "monoStarted",
+ deputyReady = "deputyReady",
ioStarted = "ioStarted",
preload = "preload",
}
export const enum MainToWorkerMessageType {
- applyConfig = "apply_mono_config",
+ applyConfig = "applyConfig",
+ allAssetsLoaded = "allAssetsLoaded",
}
export interface PThreadWorker extends Worker {
diff --git a/src/mono/mono/metadata/bundled-resources.c b/src/mono/mono/metadata/bundled-resources.c
index 7b2f80ef3edfd..17ddedb19c012 100644
--- a/src/mono/mono/metadata/bundled-resources.c
+++ b/src/mono/mono/metadata/bundled-resources.c
@@ -140,9 +140,6 @@ bundled_resources_get (const char *id);
void
mono_bundled_resources_add (MonoBundledResource **resources_to_bundle, uint32_t len)
{
- MonoDomain *domain = mono_get_root_domain ();
- g_assert (!domain);
-
if (!bundled_resources)
// FIXME: Choose a good initial capacity to avoid rehashes during startup. I picked one at random
bundled_resources = dn_simdhash_ght_new_full ((GHashFunc)bundled_resources_resource_id_hash, (GEqualFunc)bundled_resources_resource_id_equal, NULL, bundled_resources_value_destroy_func, 2048, NULL);
diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
index 3b236888404d1..c7b62fbe4ebf2 100644
--- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
+++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
@@ -355,7 +355,9 @@ Copyright (c) .NET Foundation. All rights reserved.
TargetFrameworkVersion="$(TargetFrameworkVersion)"
ModuleAfterConfigLoaded="@(WasmModuleAfterConfigLoaded)"
ModuleAfterRuntimeReady="@(WasmModuleAfterRuntimeReady)"
- IsPublish="false" />
+ IsPublish="false"
+ IsAot="$(RunAOTCompilation)"
+ IsMultiThreaded="$(WasmEnableThreads)" />
@@ -641,7 +643,9 @@ Copyright (c) .NET Foundation. All rights reserved.
TargetFrameworkVersion="$(TargetFrameworkVersion)"
ModuleAfterConfigLoaded="@(WasmModuleAfterConfigLoaded)"
ModuleAfterRuntimeReady="@(WasmModuleAfterRuntimeReady)"
- IsPublish="true" />
+ IsPublish="true"
+ IsAot="$(RunAOTCompilation)"
+ IsMultiThreaded="$(WasmEnableThreads)" />
diff --git a/src/mono/sample/wasm/browser-minimal-config/main.js b/src/mono/sample/wasm/browser-minimal-config/main.js
index acd81b2899a39..702234c984df5 100644
--- a/src/mono/sample/wasm/browser-minimal-config/main.js
+++ b/src/mono/sample/wasm/browser-minimal-config/main.js
@@ -34,11 +34,13 @@ const assets = [
},
{
name: "System.Private.CoreLib.wasm",
- behavior: "assembly"
+ behavior: "assembly",
+ isCore: true,
},
{
name: "System.Runtime.InteropServices.JavaScript.wasm",
- behavior: "assembly"
+ behavior: "assembly",
+ isCore: true,
},
{
name: "Wasm.Browser.Config.Sample.wasm",
@@ -62,10 +64,12 @@ const resources = {
"wasmNative": {
"dotnet.native.wasm": ""
},
- "assembly": {
- "System.Console.wasm": "",
+ "coreAssembly": {
"System.Private.CoreLib.wasm": "",
"System.Runtime.InteropServices.JavaScript.wasm": "",
+ },
+ "assembly": {
+ "System.Console.wasm": "",
"Wasm.Browser.Config.Sample.wasm": ""
},
};
diff --git a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
index 581a187270ae4..8e725222cbca7 100644
--- a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
@@ -404,7 +404,7 @@ public void AssertBootJson(AssertBundleOptionsBase options)
BootJsonData bootJson = ParseBootData(bootJsonPath);
string spcExpectedFilename = $"System.Private.CoreLib{WasmAssemblyExtension}";
- string? spcActualFilename = bootJson.resources.assembly.Keys
+ string? spcActualFilename = bootJson.resources.coreAssembly.Keys
.Where(a => Path.GetFileNameWithoutExtension(a) == "System.Private.CoreLib")
.SingleOrDefault();
if (spcActualFilename is null)
diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets
index 501a98264538c..54a202840b41f 100644
--- a/src/mono/wasm/build/WasmApp.Common.targets
+++ b/src/mono/wasm/build/WasmApp.Common.targets
@@ -435,7 +435,7 @@
-
+
diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js
index 3eb9e50aedeb3..e426886485d9d 100644
--- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js
+++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js
@@ -89,7 +89,7 @@ switch (testCase) {
const { setModuleImports, getAssemblyExports, getConfig, INTERNAL } = await dotnet.create();
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
-const assemblyExtension = config.resources.assembly['System.Private.CoreLib.wasm'] !== undefined ? ".wasm" : ".dll";
+const assemblyExtension = config.resources.coreAssembly['System.Private.CoreLib.wasm'] !== undefined ? ".wasm" : ".dll";
// Run the test case
try {
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs
index 17760e84aad06..6436fc1aecb93 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs
@@ -10,8 +10,29 @@
namespace Microsoft.NET.Sdk.WebAssembly
{
- public class BootJsonBuilderHelper(TaskLoggingHelper Log)
+ public class BootJsonBuilderHelper(TaskLoggingHelper Log, bool IsMultiThreaded)
{
+ private static readonly string[] coreAssemblyNames = [
+ "System.Private.CoreLib",
+ "System.Runtime.InteropServices.JavaScript",
+ ];
+
+ private static readonly string[] extraMultiThreadedCoreAssemblyName = [
+ "System.Threading.Channels"
+ ];
+
+ public bool IsCoreAssembly(string fileName)
+ {
+ var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
+ if (coreAssemblyNames.Contains(fileNameWithoutExtension))
+ return true;
+
+ if (IsMultiThreaded && extraMultiThreadedCoreAssemblyName.Contains(fileNameWithoutExtension))
+ return true;
+
+ return false;
+ }
+
public void ComputeResourcesHash(BootJsonData bootConfig)
{
var sb = new StringBuilder();
@@ -26,6 +47,7 @@ static void AddDictionary(StringBuilder sb, Dictionary? res)
}
AddDictionary(sb, bootConfig.resources.assembly);
+ AddDictionary(sb, bootConfig.resources.coreAssembly);
AddDictionary(sb, bootConfig.resources.jsModuleWorker);
AddDictionary(sb, bootConfig.resources.jsModuleNative);
@@ -48,6 +70,12 @@ static void AddDictionary(StringBuilder sb, Dictionary? res)
AddDictionary(sb, entry.Value);
}
+ if (bootConfig.resources.coreVfs != null)
+ {
+ foreach (var entry in bootConfig.resources.coreVfs)
+ AddDictionary(sb, entry.Value);
+ }
+
bootConfig.resources.hash = Utils.ComputeTextIntegrity(sb.ToString());
}
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs
index 22edc2c68fbe1..8c28db9b2d4c3 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs
@@ -152,11 +152,22 @@ public class ResourcesData
[DataMember(EmitDefaultValue = false)]
public ResourceHashesByNameDictionary icu { get; set; }
+ ///
+ /// "assembly" (.dll) resources needed to start MonoVM
+ ///
+ public ResourceHashesByNameDictionary coreAssembly { get; set; } = new ResourceHashesByNameDictionary();
+
///
/// "assembly" (.dll) resources
///
public ResourceHashesByNameDictionary assembly { get; set; } = new ResourceHashesByNameDictionary();
+ ///
+ /// "debug" (.pdb) resources needed to start MonoVM
+ ///
+ [DataMember(EmitDefaultValue = false)]
+ public ResourceHashesByNameDictionary corePdb { get; set; }
+
///
/// "debug" (.pdb) resources
///
@@ -201,6 +212,9 @@ public class ResourcesData
[DataMember(EmitDefaultValue = false)]
public Dictionary runtimeAssets { get; set; }
+ [DataMember(EmitDefaultValue = false)]
+ public Dictionary coreVfs { get; set; }
+
[DataMember(EmitDefaultValue = false)]
public Dictionary vfs { get; set; }
diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs
index d5dc83e4252df..fbe0aa7e3733e 100644
--- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs
+++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs
@@ -22,7 +22,11 @@ namespace Microsoft.NET.Sdk.WebAssembly;
public class GenerateWasmBootJson : Task
{
- private static readonly string[] jiterpreterOptions = new[] { "jiterpreter-traces-enabled", "jiterpreter-interp-entry-enabled", "jiterpreter-jit-call-enabled" };
+ private static readonly string[] jiterpreterOptions = [
+ "jiterpreter-traces-enabled",
+ "jiterpreter-interp-entry-enabled",
+ "jiterpreter-jit-call-enabled"
+ ];
[Required]
public string AssemblyPath { get; set; }
@@ -73,6 +77,10 @@ public class GenerateWasmBootJson : Task
public bool IsPublish { get; set; }
+ public bool IsAot { get; set; }
+
+ public bool IsMultiThreaded { get; set; }
+
public override bool Execute()
{
using var fileStream = File.Create(OutputPath);
@@ -93,7 +101,7 @@ public override bool Execute()
// Internal for tests
public void WriteBootJson(Stream output, string entryAssemblyName)
{
- var helper = new BootJsonBuilderHelper(Log);
+ var helper = new BootJsonBuilderHelper(Log, IsMultiThreaded);
var result = new BootJsonData
{
@@ -205,15 +213,32 @@ public void WriteBootJson(Stream output, string entryAssemblyName)
}
else
{
- Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as symbols file.", resource.ItemSpec);
- resourceData.pdb ??= new ResourceHashesByNameDictionary();
- resourceList = resourceData.pdb;
+ if (IsTargeting90OrLater() && (IsAot || helper.IsCoreAssembly(resourceName)))
+ {
+ Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as core symbols file.", resource.ItemSpec);
+ resourceData.corePdb ??= new ResourceHashesByNameDictionary();
+ resourceList = resourceData.corePdb;
+ }
+ else
+ {
+ Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as symbols file.", resource.ItemSpec);
+ resourceData.pdb ??= new ResourceHashesByNameDictionary();
+ resourceList = resourceData.pdb;
+ }
}
}
else if (string.Equals("runtime", assetTraitValue, StringComparison.OrdinalIgnoreCase))
{
- Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as an app assembly.", resource.ItemSpec);
- resourceList = resourceData.assembly;
+ if (IsTargeting90OrLater() && (IsAot || helper.IsCoreAssembly(resourceName)))
+ {
+ Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as core assembly.", resource.ItemSpec);
+ resourceList = resourceData.coreAssembly;
+ }
+ else
+ {
+ Log.LogMessage(MessageImportance.Low, "Candidate '{0}' is defined as an app assembly.", resource.ItemSpec);
+ resourceList = resourceData.assembly;
+ }
}
else if (string.Equals(assetTraitName, "WasmResource", StringComparison.OrdinalIgnoreCase) &&
string.Equals(assetTraitValue, "native", StringComparison.OrdinalIgnoreCase))
@@ -445,8 +470,15 @@ private bool TryGetLazyLoadedAssembly(string fileName, out ITaskItem lazyLoadedA
private Version? parsedTargetFrameworkVersion;
private static readonly Version version80 = new Version(8, 0);
+ private static readonly Version version90 = new Version(9, 0);
private bool IsTargeting80OrLater()
+ => IsTargetingVersionOrLater(version80);
+
+ private bool IsTargeting90OrLater()
+ => IsTargetingVersionOrLater(version90);
+
+ private bool IsTargetingVersionOrLater(Version version)
{
if (parsedTargetFrameworkVersion == null)
{
@@ -457,6 +489,6 @@ private bool IsTargeting80OrLater()
parsedTargetFrameworkVersion = Version.Parse(tfv);
}
- return parsedTargetFrameworkVersion >= version80;
+ return parsedTargetFrameworkVersion >= version;
}
}
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
index 7847039163b11..fa5cf03f9a560 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
@@ -31,6 +31,8 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask
public string? RuntimeAssetsLocation { get; set; }
public bool CacheBootResources { get; set; }
public int DebugLevel { get; set; }
+ public bool IsAot { get; set; }
+ public bool IsMultiThreaded { get; set; }
private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions
{
@@ -95,7 +97,7 @@ private GlobalizationMode GetGlobalizationMode()
protected override bool ExecuteInternal()
{
- var helper = new BootJsonBuilderHelper(Log);
+ var helper = new BootJsonBuilderHelper(Log, IsMultiThreaded);
var logAdapter = new LogAdapter(Log);
if (!ValidateArguments())
@@ -206,16 +208,30 @@ protected override bool ExecuteInternal()
bytes = File.ReadAllBytes(assemblyPath);
}
- bootConfig.resources.assembly[Path.GetFileName(assemblyPath)] = Utils.ComputeIntegrity(bytes);
+ var assemblyName = Path.GetFileName(assemblyPath);
+ bool isCoreAssembly = IsAot || helper.IsCoreAssembly(assemblyName);
+
+ var assemblyList = isCoreAssembly ? bootConfig.resources.coreAssembly : bootConfig.resources.assembly;
+ assemblyList[assemblyName] = Utils.ComputeIntegrity(bytes);
+
if (DebugLevel != 0)
{
var pdb = Path.ChangeExtension(assembly, ".pdb");
if (File.Exists(pdb))
{
- if (bootConfig.resources.pdb == null)
- bootConfig.resources.pdb = new();
-
- bootConfig.resources.pdb[Path.GetFileName(pdb)] = Utils.ComputeIntegrity(pdb);
+ if (isCoreAssembly)
+ {
+ if (bootConfig.resources.corePdb == null)
+ bootConfig.resources.corePdb = new();
+ }
+ else
+ {
+ if (bootConfig.resources.pdb == null)
+ bootConfig.resources.pdb = new();
+ }
+
+ var pdbList = isCoreAssembly ? bootConfig.resources.corePdb : bootConfig.resources.pdb;
+ pdbList[Path.GetFileName(pdb)] = Utils.ComputeIntegrity(pdb);
}
}
}
@@ -268,9 +284,11 @@ protected override bool ExecuteInternal()
var i = 0;
StringDictionary targetPathTable = new();
var vfs = new Dictionary>();
+ var coreVfs = new Dictionary>();
foreach (var item in FilesToIncludeInFileSystem)
{
string? targetPath = item.GetMetadata("TargetPath");
+ string? loadingStage = item.GetMetadata("LoadingStage");
if (string.IsNullOrEmpty(targetPath))
{
targetPath = Path.GetFileName(item.ItemSpec);
@@ -298,7 +316,14 @@ protected override bool ExecuteInternal()
var vfsPath = Path.Combine(supportFilesDir, generatedFileName);
FileCopyChecked(item.ItemSpec, vfsPath, "FilesToIncludeInFileSystem");
- vfs[targetPath] = new()
+ var vfsDict = loadingStage switch
+ {
+ null => vfs,
+ "" => vfs,
+ "Core" => coreVfs,
+ _ => throw new LogAsErrorException($"The WasmFilesToIncludeInFileSystem '{item.ItemSpec}' has LoadingStage set to unsupported '{loadingStage}' (empty or 'Core' is currently supported).")
+ };
+ vfsDict[targetPath] = new()
{
[$"supportFiles/{generatedFileName}"] = Utils.ComputeIntegrity(vfsPath)
};
@@ -306,6 +331,9 @@ protected override bool ExecuteInternal()
if (vfs.Count > 0)
bootConfig.resources.vfs = vfs;
+
+ if (coreVfs.Count > 0)
+ bootConfig.resources.coreVfs = coreVfs;
}
if (!InvariantGlobalization)