Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/mono/sample/wasm/browser-advanced/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ try {
},
postRun: () => { console.log('user code Module.postRun'); },
})
.withResourceLoader((type, name, defaultUri, integrity, behavior) => {
// loadBootResource could return string with unqualified name of resource. It assumes that we resolve it with document.baseURI
return `${name}`;
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
})
.create();

// at this point both emscripten and monoVM are fully initialized.
Expand Down
18 changes: 18 additions & 0 deletions src/mono/sample/wasm/browser-minimal-config/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.InteropServices;

namespace Sample
{
public partial class Test
{
public static int Main(string[] args)
{
Console.WriteLine("Hello minimal config");
return 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\DefaultBrowserSample.targets" />
<ItemGroup>
<WasmExtraFilesToDeploy Include="main.js" />
</ItemGroup>
</Project>
17 changes: 17 additions & 0 deletions src/mono/sample/wasm/browser-minimal-config/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<!-- Licensed to the .NET Foundation under one or more agreements. -->
<!-- The .NET Foundation licenses this file to you under the MIT license. -->
<html>

<head>
<title>Wasm Browser Sample</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type='module' src="./main.js"></script>
</head>

<body>
<h3 id="header">Wasm Browser Minimal Config Sample</h3>
</body>

</html>
69 changes: 69 additions & 0 deletions src/mono/sample/wasm/browser-minimal-config/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { dotnet } from "./_framework/dotnet.js";

async function fetchBinary(uri) {
return new Uint8Array(await (await fetch(uri)).arrayBuffer());
}

const assets = [
{
name: "dotnet.native.js",
behavior: "js-module-native"
},
{
name: "dotnet.js",
behavior: "js-module-dotnet"
},
{
name: "dotnet.runtime.js",
behavior: "js-module-runtime"
},
{
name: "dotnet.native.wasm",
behavior: "dotnetwasm"
},
{
name: "System.Private.CoreLib.wasm",
behavior: "assembly"
},
{
name: "System.Runtime.InteropServices.JavaScript.wasm",
behavior: "assembly"
},
{
name: "Wasm.Browser.Config.Sample.wasm",
buffer: await fetchBinary("./_framework/Wasm.Browser.Config.Sample.wasm"),
behavior: "assembly"
},
{
name: "System.Console.wasm",
behavior: "assembly"
},
];

const resources = {
"jsModuleNative": {
"dotnet.native.js": ""
},
"jsModuleRuntime": {
"dotnet.runtime.js": ""
},
"wasmNative": {
"dotnet.native.wasm": ""
},
"assembly": {
"System.Console.wasm": "",
"System.Private.CoreLib.wasm": "",
"System.Runtime.InteropServices.JavaScript.wasm": "",
"Wasm.Browser.Config.Sample.wasm": ""
},
};

const config = {
"mainAssemblyName": "Wasm.Browser.Config.Sample.dll",
assets,
};

await dotnet
.withConfig(config)
.withElementOnExit()
.run();
2 changes: 1 addition & 1 deletion src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ public void AssertBootJson(AssertBundleOptionsBase options)
var bootJsonEntries = bootJson.resources.jsModuleNative.Keys
.Union(bootJson.resources.jsModuleRuntime.Keys)
.Union(bootJson.resources.jsModuleWorker?.Keys ?? Enumerable.Empty<string>())
.Union(bootJson.resources.jsSymbols?.Keys ?? Enumerable.Empty<string>())
.Union(bootJson.resources.wasmSymbols?.Keys ?? Enumerable.Empty<string>())
.Union(bootJson.resources.wasmNative.Keys)
.ToArray();

Expand Down
22 changes: 16 additions & 6 deletions src/mono/wasm/runtime/dotnet.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ interface ResourceGroups {
jsModuleWorker?: ResourceList;
jsModuleNative: ResourceList;
jsModuleRuntime: ResourceList;
jsSymbols?: ResourceList;
wasmSymbols?: ResourceList;
wasmNative: ResourceList;
icu?: ResourceList;
satelliteResources?: {
Expand All @@ -208,8 +208,9 @@ type ResourceList = {
* @param defaultUri The URI from which the framework would fetch the resource by default. The URI may be relative or absolute.
* @param integrity The integrity string representing the expected content in the response.
* @returns A URI string or a Response promise to override the loading process, or null/undefined to allow the default loading behavior.
* When returned string is not qualified with `./` or absolute URL, it will be resolved against the application base URI.
*/
type LoadBootResourceCallback = (type: AssetBehaviors | "manifest", name: string, defaultUri: string, integrity: string) => string | Promise<Response> | null | undefined;
type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string, behavior?: AssetBehaviors) => string | Promise<Response> | null | undefined;
interface ResourceRequest {
name: string;
behavior: AssetBehaviors;
Expand Down Expand Up @@ -254,18 +255,26 @@ type SingleAssetBehaviors =
* The binary of the dotnet runtime.
*/
"dotnetwasm"
/**
* The javascript module for loader.
*/
| "js-module-dotnet"
/**
* The javascript module for threads.
*/
| "js-module-threads"
/**
* The javascript module for threads.
* The javascript module for runtime.
*/
| "js-module-runtime"
/**
* The javascript module for threads.
* The javascript module for emscripten.
*/
| "js-module-native"
/**
* Typically blazor.boot.json
*/
| "js-module-native";
| "manifest";
type AssetBehaviors = SingleAssetBehaviors |
/**
* Load asset as a managed resource assembly.
Expand Down Expand Up @@ -396,6 +405,7 @@ type ModuleAPI = {
exit: (code: number, reason?: any) => void;
};
type CreateDotnetRuntimeType = (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) => Promise<RuntimeAPI>;
type WebAssemblyBootResourceType = "assembly" | "pdb" | "dotnetjs" | "dotnetwasm" | "globalization" | "manifest" | "configuration";

interface IDisposable {
dispose(): void;
Expand Down Expand Up @@ -431,4 +441,4 @@ declare global {
}
declare const createDotnetRuntime: CreateDotnetRuntimeType;

export { AssetEntry, CreateDotnetRuntimeType, DotnetHostBuilder, DotnetModuleConfig, EmscriptenModule, GlobalizationMode, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit };
export { AssetBehaviors, AssetEntry, CreateDotnetRuntimeType, DotnetHostBuilder, DotnetModuleConfig, EmscriptenModule, GlobalizationMode, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit };
92 changes: 44 additions & 48 deletions src/mono/wasm/runtime/loader/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { mono_log_debug } from "./logging";
import { mono_exit } from "./exit";
import { addCachedReponse, findCachedResponse, isCacheAvailable } from "./assetsCache";
import { getIcuResourceName } from "./icu";
import { mono_log_warn } from "./logging";
import { makeURLAbsoluteWithApplicationBase } from "./polyfills";


let throttlingPromise: PromiseAndController<void> | undefined;
Expand All @@ -20,6 +22,7 @@ const jsModulesAssetTypes: {
} = {
"js-module-threads": true,
"js-module-runtime": true,
"js-module-dotnet": true,
"js-module-native": true,
};

Expand Down Expand Up @@ -78,10 +81,10 @@ function getSingleAssetWithResolvedUrl(resources: ResourceList | undefined, beha

const customSrc = invokeLoadBootResource(asset);
if (typeof (customSrc) === "string") {
asset.resolvedUrl = customSrc;
asset.resolvedUrl = makeURLAbsoluteWithApplicationBase(customSrc);
} else if (customSrc) {
// Since we must load this via a import, it's only valid to supply a URI (and not a Request, say)
throw new Error(`For a ${behavior} resource, custom loaders must supply a URI string.`);
mono_log_warn(`For a ${behavior} resource, custom loaders must supply a URI string.`);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
// we apply a default URL
}

return asset;
Expand Down Expand Up @@ -164,8 +167,9 @@ export async function mono_download_assets(): Promise<void> {
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");
mono_assert(typeof asset.resolvedUrl === "string", "resolvedUrl must be string");
const url = asset.resolvedUrl!;
const data = new Uint8Array(asset.buffer!);
cleanupAsset(asset);

Expand Down Expand Up @@ -220,8 +224,25 @@ export async function mono_download_assets(): Promise<void> {

function prepareAssets(containedInSnapshotAssets: AssetEntryInternal[], alwaysLoadedAssets: AssetEntryInternal[]) {
const config = loaderHelpers.config;
const resources = loaderHelpers.config.resources;
if (resources) {

// if assets exits, we will assume Net7 legacy and not process resources object
if (config.assets) {
for (const a of config.assets) {
const asset: AssetEntryInternal = a;
mono_assert(typeof asset === "object", "asset must be object");
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
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);
}
}
} else if (config.resources) {
const resources = config.resources;
if (resources.assembly) {
for (const name in resources.assembly) {
containedInSnapshotAssets.push({
Expand Down Expand Up @@ -282,11 +303,11 @@ function prepareAssets(containedInSnapshotAssets: AssetEntryInternal[], alwaysLo
}
}

if (resources.jsSymbols) {
for (const name in resources.jsSymbols) {
if (resources.wasmSymbols) {
for (const name in resources.wasmSymbols) {
alwaysLoadedAssets.push({
name,
hash: resources.jsSymbols[name],
hash: resources.wasmSymbols[name],
behavior: "symbols"
});
}
Expand All @@ -307,31 +328,7 @@ function prepareAssets(containedInSnapshotAssets: AssetEntryInternal[], alwaysLo
}
}

const newAssets = [...containedInSnapshotAssets, ...alwaysLoadedAssets];

if (loaderHelpers.config.assets) {
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);
}
}
}

if (!loaderHelpers.config.assets) {
loaderHelpers.config.assets = [];
}

loaderHelpers.config.assets = [...loaderHelpers.config.assets, ...newAssets];

config.assets = [...containedInSnapshotAssets, ...alwaysLoadedAssets];
}

export function delay(ms: number): Promise<void> {
Expand Down Expand Up @@ -431,12 +428,17 @@ async function start_asset_download_sources(asset: AssetEntryInternal): Promise<
}
if (asset.buffer) {
const buffer = asset.buffer;
asset.buffer = null as any; // GC
if (!asset.resolvedUrl) {
asset.resolvedUrl = "undefined://" + asset.name;
}
asset.pendingDownloadInternal = {
url: "undefined://" + asset.name,
url: asset.resolvedUrl,
name: asset.name,
response: Promise.resolve({
ok: true,
arrayBuffer: () => buffer,
json: () => JSON.parse(new TextDecoder("utf-8").decode(buffer)),
text: () => { throw new Error("NotImplementedException"); },
headers: {
get: () => undefined,
}
Expand Down Expand Up @@ -585,8 +587,7 @@ function fetchResource(request: ResourceRequest): Promise<Response> {
// They are supplying an entire custom response, so just use that
return customLoadResult;
} else if (typeof customLoadResult === "string") {
// They are supplying a custom URL, so use that with the default fetch behavior
url = customLoadResult;
url = makeURLAbsoluteWithApplicationBase(customLoadResult);
}
}

Expand Down Expand Up @@ -614,7 +615,9 @@ const monoToBlazorAssetTypeMap: { [key: string]: WebAssemblyBootResourceType | u
"pdb": "pdb",
"icu": "globalization",
"vfs": "configuration",
"manifest": "manifest",
"dotnetwasm": "dotnetwasm",
"js-module-dotnet": "dotnetjs",
"js-module-native": "dotnetjs",
"js-module-runtime": "dotnetjs",
"js-module-threads": "dotnetjs"
Expand All @@ -625,17 +628,10 @@ function invokeLoadBootResource(request: ResourceRequest): string | Promise<Resp
const requestHash = request.hash ?? "";
const url = request.resolvedUrl!;

// Try to send with AssetBehaviors
let customLoadResult = loaderHelpers.loadBootResource(request.behavior, request.name, url, requestHash);
if (!customLoadResult) {
// If we don't get result, try to send with WebAssemblyBootResourceType
const resourceType = monoToBlazorAssetTypeMap[request.behavior];
if (resourceType) {
customLoadResult = loaderHelpers.loadBootResource(resourceType as AssetBehaviors, request.name, url, requestHash);
}
const resourceType = monoToBlazorAssetTypeMap[request.behavior];
if (resourceType) {
return loaderHelpers.loadBootResource(resourceType, request.name, url, requestHash, request.behavior);
}

return customLoadResult;
}

return undefined;
Expand Down
Loading