Skip to content

Commit 8f9e90b

Browse files
committed
fetchWasm
1 parent 21418b0 commit 8f9e90b

File tree

6 files changed

+163
-114
lines changed

6 files changed

+163
-114
lines changed

src/mono/sample/wasm/browser/wwwroot/main.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
import { dotnet, exit } from './_framework/dotnet.js'
5-
import { config } from './_framework/dotnet.boot.js';
65

76
function displayMeaning(meaning) {
87
console.log(`Meaning of life is ${meaning}`);
@@ -15,7 +14,6 @@ function delay(ms) {
1514

1615
try {
1716
const { setModuleImports, getAssemblyExports, runMain } = await dotnet
18-
.withConfig(config)
1917
.withElementOnExit()
2018
.withExitOnUnhandledError()
2119
.create();
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
import type { LoadBootResourceCallback, JsModuleExports, JsAsset, AssemblyAsset, PdbAsset, WasmAsset, IcuAsset, EmscriptenModuleInternal, InstantiateWasmSuccessCallback } from "./types";
5+
import { dotnetAssert, dotnetGetInternals, dotnetBrowserHostExports, dotnetUpdateInternals } from "./cross-module";
6+
import { ENVIRONMENT_IS_NODE } from "./per-module";
7+
import { getIcuResourceName } from "./icu";
8+
import { getLoaderConfig } from "./config";
9+
import { BrowserHost_InitializeCoreCLR } from "./run";
10+
import { createPromiseCompletionSource } from "./promise-completion-source";
11+
import { locateFile } from "./bootstrap";
12+
13+
const nativeModulePromiseController = createPromiseCompletionSource<EmscriptenModuleInternal>(() => {
14+
dotnetUpdateInternals(dotnetGetInternals());
15+
});
16+
let wasmBinaryPromise: Promise<Response> = undefined as any;
17+
18+
// WASM-TODO: retry logic
19+
// WASM-TODO: throttling logic
20+
// WASM-TODO: invokeLibraryInitializers
21+
// WASM-TODO: webCIL
22+
// WASM-TODO: downloadOnly - blazor render mode auto pre-download. Really no start.
23+
24+
export async function createRuntime(downloadOnly: boolean, loadBootResource?: LoadBootResourceCallback): Promise<any> {
25+
if (loadBootResource) throw new Error("TODO: loadBootResource is not implemented yet");
26+
const config = getLoaderConfig();
27+
if (!config.resources || !config.resources.coreAssembly || !config.resources.coreAssembly.length) throw new Error("Invalid config, resources is not set");
28+
29+
const nativeModulePromise = loadJSModule(config.resources.jsModuleNative[0]);
30+
const runtimeModulePromise = loadJSModule(config.resources.jsModuleRuntime[0]);
31+
const wasmNativePromise = fetchWasm(config.resources.wasmNative[0]);
32+
33+
const coreAssembliesPromise = Promise.all(config.resources.coreAssembly.map(fetchDll));
34+
const coreVfsPromise = Promise.all((config.resources.coreVfs || []).map(fetchVfs));
35+
const assembliesPromise = Promise.all(config.resources.assembly.map(fetchDll));
36+
const vfsPromise = Promise.all((config.resources.vfs || []).map(fetchVfs));
37+
const icuResourceName = getIcuResourceName(config);
38+
const icuDataPromise = icuResourceName ? Promise.all((config.resources.icu || []).filter(asset => asset.name === icuResourceName).map(fetchIcu)) : Promise.resolve([]);
39+
40+
const nativeModule = await nativeModulePromise;
41+
const modulePromise = nativeModule.dotnetInitializeModule<EmscriptenModuleInternal>(dotnetGetInternals());
42+
nativeModulePromiseController.propagateFrom(modulePromise);
43+
44+
const runtimeModule = await runtimeModulePromise;
45+
const runtimeModuleReady = runtimeModule.dotnetInitializeModule<void>(dotnetGetInternals());
46+
47+
await nativeModulePromiseController.promise;
48+
await coreAssembliesPromise;
49+
await coreVfsPromise;
50+
await vfsPromise;
51+
await icuDataPromise;
52+
await wasmNativePromise; // this is just to propagate errors
53+
if (!downloadOnly) {
54+
BrowserHost_InitializeCoreCLR();
55+
}
56+
57+
await assembliesPromise;
58+
await runtimeModuleReady;
59+
}
60+
61+
async function loadJSModule(asset: JsAsset): Promise<JsModuleExports> {
62+
if (asset.name && !asset.resolvedUrl) {
63+
asset.resolvedUrl = locateFile(asset.name);
64+
}
65+
if (!asset.resolvedUrl) throw new Error("Invalid config, resources is not set");
66+
return await import(/* webpackIgnore: true */ asset.resolvedUrl);
67+
}
68+
69+
function fetchWasm(asset: WasmAsset): Promise<Response> {
70+
if (asset.name && !asset.resolvedUrl) {
71+
asset.resolvedUrl = locateFile(asset.name);
72+
}
73+
if (!asset.resolvedUrl) throw new Error("Invalid config, resources is not set");
74+
wasmBinaryPromise = fetchLike(asset.resolvedUrl);
75+
return wasmBinaryPromise;
76+
}
77+
78+
export async function instantiateWasm(imports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback): Promise<void> {
79+
const res = await WebAssembly.instantiateStreaming(wasmBinaryPromise, imports);
80+
successCallback(res.instance, res.module);
81+
}
82+
83+
84+
async function fetchIcu(asset: IcuAsset): Promise<void> {
85+
if (asset.name && !asset.resolvedUrl) {
86+
asset.resolvedUrl = locateFile(asset.name);
87+
}
88+
const bytes = await fetchBytes(asset);
89+
await nativeModulePromiseController.promise;
90+
dotnetBrowserHostExports.loadIcuData(bytes);
91+
}
92+
93+
async function fetchDll(asset: AssemblyAsset): Promise<void> {
94+
if (asset.name && !asset.resolvedUrl) {
95+
asset.resolvedUrl = locateFile(asset.name);
96+
}
97+
const bytes = await fetchBytes(asset);
98+
await nativeModulePromiseController.promise;
99+
100+
dotnetBrowserHostExports.registerDllBytes(bytes, asset);
101+
}
102+
103+
async function fetchVfs(asset: AssemblyAsset): Promise<void> {
104+
if (asset.name && !asset.resolvedUrl) {
105+
asset.resolvedUrl = locateFile(asset.name);
106+
}
107+
const bytes = await fetchBytes(asset);
108+
await nativeModulePromiseController.promise;
109+
110+
dotnetBrowserHostExports.installVfsFile(bytes, asset);
111+
}
112+
113+
async function fetchBytes(asset: WasmAsset | AssemblyAsset | PdbAsset | IcuAsset): Promise<Uint8Array> {
114+
dotnetAssert.check(asset && asset.resolvedUrl, "Bad asset.resolvedUrl");
115+
const response = await fetchLike(asset.resolvedUrl);
116+
const buffer = await response.arrayBuffer();
117+
return new Uint8Array(buffer);
118+
}
119+
120+
async function fetchLike(resolvedUrl: string): Promise<Response> {
121+
dotnetAssert.check(resolvedUrl, "Bad resolvedUrl");
122+
if (ENVIRONMENT_IS_NODE) {
123+
const { promises: fs } = await import(/*! webpackIgnore: true */"fs");
124+
const { fileURLToPath } = await import(/*! webpackIgnore: true */"url");
125+
const isFileUrl = resolvedUrl.startsWith("file://");
126+
if (isFileUrl) {
127+
resolvedUrl = fileURLToPath(resolvedUrl);
128+
}
129+
const arrayBuffer = await fs.readFile(resolvedUrl) as any;
130+
return <Response><any>{
131+
ok: true,
132+
headers: {
133+
length: 0,
134+
get: () => null
135+
},
136+
url: resolvedUrl,
137+
arrayBuffer: () => arrayBuffer,
138+
json: () => JSON.parse(arrayBuffer),
139+
text: () => {
140+
throw new Error("NotImplementedException");
141+
}
142+
};
143+
} else {
144+
const response = await fetch(resolvedUrl);
145+
if (!response.ok) {
146+
throw new Error(`Failed to load ${resolvedUrl} with ${response.status} ${response.statusText}`);
147+
}
148+
return response;
149+
}
150+
151+
}

src/native/corehost/browserhost/loader/bootstrap.ts

Lines changed: 2 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
import type { LoadBootResourceCallback, JsModuleExports, JsAsset, AssemblyAsset, PdbAsset, WasmAsset, IcuAsset, EmscriptenModuleInternal, LoaderConfig, DotnetHostBuilder } from "./types";
5-
import { dotnetAssert, dotnetGetInternals, dotnetBrowserHostExports, dotnetUpdateInternals } from "./cross-module";
4+
import type { LoaderConfig, DotnetHostBuilder } from "./types";
65
import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL } from "./per-module";
7-
import { getIcuResourceName } from "./icu";
8-
import { getLoaderConfig } from "./config";
9-
import { BrowserHost_InitializeCoreCLR } from "./run";
10-
import { createPromiseCompletionSource } from "./promise-completion-source";
116
import { nodeFs } from "./polyfills";
127

138
const scriptUrlQuery = /*! webpackIgnore: true */import.meta.url;
@@ -16,110 +11,7 @@ const modulesUniqueQuery = queryIndex > 0 ? scriptUrlQuery.substring(queryIndex)
1611
const scriptUrl = normalizeFileUrl(scriptUrlQuery);
1712
const scriptDirectory = normalizeDirectoryUrl(scriptUrl);
1813

19-
const nativeModulePromiseController = createPromiseCompletionSource<EmscriptenModuleInternal>(() => {
20-
dotnetUpdateInternals(dotnetGetInternals());
21-
});
22-
23-
// WASM-TODO: retry logic
24-
// WASM-TODO: throttling logic
25-
// WASM-TODO: invokeLibraryInitializers
26-
// WASM-TODO: webCIL
27-
// WASM-TODO: downloadOnly - blazor render mode auto pre-download. Really no start.
28-
29-
export async function createRuntime(downloadOnly: boolean, loadBootResource?: LoadBootResourceCallback): Promise<any> {
30-
const config = getLoaderConfig();
31-
if (!config.resources || !config.resources.coreAssembly || !config.resources.coreAssembly.length) throw new Error("Invalid config, resources is not set");
32-
33-
const nativeModulePromise = loadJSModule(config.resources.jsModuleNative[0], loadBootResource);
34-
const runtimeModulePromise = loadJSModule(config.resources.jsModuleRuntime[0], loadBootResource);
35-
const coreAssembliesPromise = Promise.all(config.resources.coreAssembly.map(fetchDll));
36-
const coreVfsPromise = Promise.all((config.resources.coreVfs || []).map(fetchVfs));
37-
const assembliesPromise = Promise.all(config.resources.assembly.map(fetchDll));
38-
const vfsPromise = Promise.all((config.resources.vfs || []).map(fetchVfs));
39-
const icuResourceName = getIcuResourceName(config);
40-
const icuDataPromise = icuResourceName ? Promise.all((config.resources.icu || []).filter(asset => asset.name === icuResourceName).map(fetchIcu)) : Promise.resolve([]);
41-
// WASM-TODO fetchWasm(config.resources.wasmNative[0]);// start loading early, no await
42-
43-
const nativeModule = await nativeModulePromise;
44-
const modulePromise = nativeModule.dotnetInitializeModule<EmscriptenModuleInternal>(dotnetGetInternals());
45-
nativeModulePromiseController.propagateFrom(modulePromise);
46-
47-
const runtimeModule = await runtimeModulePromise;
48-
const runtimeModuleReady = runtimeModule.dotnetInitializeModule<void>(dotnetGetInternals());
49-
50-
await nativeModulePromiseController.promise;
51-
await coreAssembliesPromise;
52-
await coreVfsPromise;
53-
await vfsPromise;
54-
await icuDataPromise;
55-
if (!downloadOnly) {
56-
BrowserHost_InitializeCoreCLR();
57-
}
58-
59-
await assembliesPromise;
60-
await runtimeModuleReady;
61-
}
62-
63-
async function loadJSModule(asset: JsAsset, loadBootResource?: LoadBootResourceCallback): Promise<JsModuleExports> {
64-
if (loadBootResource) throw new Error("TODO: loadBootResource is not implemented yet");
65-
if (asset.name && !asset.resolvedUrl) {
66-
asset.resolvedUrl = locateFile(asset.name);
67-
}
68-
if (!asset.resolvedUrl) throw new Error("Invalid config, resources is not set");
69-
return await import(/* webpackIgnore: true */ asset.resolvedUrl);
70-
}
71-
72-
async function fetchIcu(asset: IcuAsset): Promise<void> {
73-
if (asset.name && !asset.resolvedUrl) {
74-
asset.resolvedUrl = locateFile(asset.name);
75-
}
76-
const bytes = await fetchBytes(asset);
77-
await nativeModulePromiseController.promise;
78-
dotnetBrowserHostExports.loadIcuData(bytes);
79-
}
80-
81-
async function fetchDll(asset: AssemblyAsset): Promise<void> {
82-
if (asset.name && !asset.resolvedUrl) {
83-
asset.resolvedUrl = locateFile(asset.name);
84-
}
85-
const bytes = await fetchBytes(asset);
86-
await nativeModulePromiseController.promise;
87-
88-
dotnetBrowserHostExports.registerDllBytes(bytes, asset);
89-
}
90-
91-
async function fetchVfs(asset: AssemblyAsset): Promise<void> {
92-
if (asset.name && !asset.resolvedUrl) {
93-
asset.resolvedUrl = locateFile(asset.name);
94-
}
95-
const bytes = await fetchBytes(asset);
96-
await nativeModulePromiseController.promise;
97-
98-
dotnetBrowserHostExports.installVfsFile(bytes, asset);
99-
}
100-
101-
async function fetchBytes(asset: WasmAsset | AssemblyAsset | PdbAsset | IcuAsset): Promise<Uint8Array> {
102-
dotnetAssert.check(asset && asset.resolvedUrl, "Bad asset.resolvedUrl");
103-
if (ENVIRONMENT_IS_NODE) {
104-
const { promises: fs } = await import("fs");
105-
const { fileURLToPath } = await import(/*! webpackIgnore: true */"url");
106-
const isFileUrl = asset.resolvedUrl!.startsWith("file://");
107-
if (isFileUrl) {
108-
asset.resolvedUrl = fileURLToPath(asset.resolvedUrl!);
109-
}
110-
const buffer = await fs.readFile(asset.resolvedUrl!);
111-
return new Uint8Array(buffer);
112-
} else {
113-
const response = await fetch(asset.resolvedUrl!);
114-
if (!response.ok) {
115-
throw new Error(`Failed to load ${asset.resolvedUrl} with ${response.status} ${response.statusText}`);
116-
}
117-
const buffer = await response.arrayBuffer();
118-
return new Uint8Array(buffer);
119-
}
120-
}
121-
122-
function locateFile(path: string) {
14+
export function locateFile(path: string) {
12315
if ("URL" in globalThis) {
12416
return new URL(path, scriptDirectory).toString();
12517
}

src/native/corehost/browserhost/loader/host-builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { DotnetHostBuilder, LoaderConfig, RuntimeAPI, LoadBootResourceCallb
55

66
import { Module, dotnetApi } from "./cross-module";
77
import { getLoaderConfig, mergeLoaderConfig, validateLoaderConfig } from "./config";
8-
import { createRuntime } from "./bootstrap";
8+
import { createRuntime } from "./assets";
99
import { exit } from "./exit";
1010

1111
let applicationArguments: string[] | undefined = [];

src/native/corehost/browserhost/loader/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
import type {
55
LoggerType, AssertType, RuntimeAPI, LoaderExports,
6-
NativeBrowserExportsTable, LoaderExportsTable, RuntimeExportsTable, InternalExchange, BrowserHostExportsTable, InteropJavaScriptExportsTable, BrowserUtilsExportsTable
6+
NativeBrowserExportsTable, LoaderExportsTable, RuntimeExportsTable, InternalExchange, BrowserHostExportsTable, InteropJavaScriptExportsTable, BrowserUtilsExportsTable,
7+
EmscriptenModuleInternal
78
} from "./types";
89
import { InternalExchangeIndex } from "../types";
910

@@ -19,6 +20,7 @@ import { check, error, info, warn, debug } from "./logging";
1920
import { dotnetAssert, dotnetLoaderExports, dotnetLogger, dotnetUpdateInternals, dotnetUpdateInternalsSubscriber } from "./cross-module";
2021
import { rejectRunMainPromise, resolveRunMainPromise, getRunMainPromise } from "./run";
2122
import { createPromiseCompletionSource, getPromiseCompletionSource, isControllablePromise } from "./promise-completion-source";
23+
import { instantiateWasm } from "./assets";
2224

2325
export function dotnetInitializeModule(): RuntimeAPI {
2426

@@ -71,6 +73,11 @@ export function dotnetInitializeModule(): RuntimeAPI {
7173
};
7274
Object.assign(dotnetAssert, assert);
7375

76+
const localModule: Partial<EmscriptenModuleInternal> = {
77+
instantiateWasm,
78+
};
79+
Object.assign(dotnetApi.Module!, localModule);
80+
7481
internals[InternalExchangeIndex.LoaderExportsTable] = loaderExportsToTable(dotnetLogger, dotnetAssert, dotnetLoaderExports);
7582
dotnetUpdateInternals(internals, dotnetUpdateInternalsSubscriber);
7683
return dotnetApi as RuntimeAPI;

src/native/libs/Common/JavaScript/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ set(ROLLUP_TS_SOURCES
2222
"${CLR_SRC_NATIVE_DIR}/corehost/browserhost/host/host.ts"
2323
"${CLR_SRC_NATIVE_DIR}/corehost/browserhost/host/index.ts"
2424
"${CLR_SRC_NATIVE_DIR}/corehost/browserhost/host/types.ts"
25+
"${CLR_SRC_NATIVE_DIR}/corehost/browserhost/loader/assets.ts"
2526
"${CLR_SRC_NATIVE_DIR}/corehost/browserhost/loader/bootstrap.ts"
2627
"${CLR_SRC_NATIVE_DIR}/corehost/browserhost/loader/config.ts"
2728
"${CLR_SRC_NATIVE_DIR}/corehost/browserhost/loader/cross-module.ts"

0 commit comments

Comments
 (0)