From d595da4d9ae287ac5b21ca0026544a21b0dc80c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 22 Jun 2023 10:14:44 +0200 Subject: [PATCH 01/14] Remove duplicate boot config download and import 'dotnet.js' --- .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 4f465f62075c..a2beade7d429 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -191,22 +191,12 @@ async function importDotnetJs(startOptions: Partial): P throw new Error('This browser does not support WebAssembly.'); } - const bootConfig = await loadBootConfigAsync(startOptions.loadBootResource, startOptions.environment); - - // The dotnet.*.js file has a version or hash in its name as a form of cache-busting. This is needed - // because it's the only part of the loading process that can't use cache:'no-cache' (because it's - // not a 'fetch') and isn't controllable by the developer (so they can't put in their own cache-busting - // querystring). So, to find out the exact URL we have to search the boot manifest. - const dotnetJsResourceName = Object - .keys(bootConfig.resources.runtime) - .filter(n => n.startsWith('dotnet.') && n.endsWith('.js'))[0]; - const dotnetJsContentHash = bootConfig.resources.runtime[dotnetJsResourceName]; - let src = `_framework/${dotnetJsResourceName}`; + let src = '_framework/dotnet.js'; // Allow overriding the URI from which the dotnet.*.js file is loaded if (startOptions.loadBootResource) { const resourceType: WebAssemblyBootResourceType = 'dotnetjs'; - const customSrc = startOptions.loadBootResource(resourceType, dotnetJsResourceName, src, dotnetJsContentHash); + const customSrc = startOptions.loadBootResource(resourceType, 'dotnet.js', src, ''); if (typeof (customSrc) === 'string') { src = customSrc; } else if (customSrc) { @@ -215,19 +205,6 @@ async function importDotnetJs(startOptions: Partial): P } } - // For consistency with WebAssemblyResourceLoader, we only enforce SRI if caching is allowed - if (bootConfig.cacheBootResources) { - const scriptElem = document.createElement('link'); - scriptElem.rel = 'modulepreload'; - scriptElem.href = src; - scriptElem.crossOrigin = 'anonymous'; - // it will make dynamic import fail if the hash doesn't match - // It's currently only validated by chromium browsers - // Firefox doesn't break on it, but doesn't validate it either - scriptElem.integrity = dotnetJsContentHash; - document.head.appendChild(scriptElem); - } - const absoluteSrc = (new URL(src, document.baseURI)).toString(); return await import(/* webpackIgnore: true */ absoluteSrc); } From dd3d8dadc366b86803af0eff9e8829a86137c825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 23 Jun 2023 14:02:26 +0200 Subject: [PATCH 02/14] Remove loadBootConfigAsync --- .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index a2beade7d429..6c17e7cf4c8b 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -150,41 +150,6 @@ export const monoPlatform: Platform = { }, }; -type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) => string | Promise | null | undefined; - -async function loadBootConfigAsync(loadBootResource?: LoadBootResourceCallback, environment?: string): Promise { - const loaderResponse = loadBootResource !== undefined ? - loadBootResource('manifest', 'blazor.boot.json', '_framework/blazor.boot.json', '') : - defaultLoadBlazorBootJson('_framework/blazor.boot.json'); - - let bootConfigResponse: Response; - - if (!loaderResponse) { - bootConfigResponse = await defaultLoadBlazorBootJson('_framework/blazor.boot.json'); - } else if (typeof loaderResponse === 'string') { - bootConfigResponse = await defaultLoadBlazorBootJson(loaderResponse); - } else { - bootConfigResponse = await loaderResponse; - } - - // While we can expect an ASP.NET Core hosted application to include the environment, other - // hosts may not. Assume 'Production' in the absence of any specified value. - applicationEnvironment = environment || bootConfigResponse.headers.get('Blazor-Environment') || 'Production'; - const bootConfig: BootJsonData = await bootConfigResponse.json(); - bootConfig.modifiableAssemblies = bootConfigResponse.headers.get('DOTNET-MODIFIABLE-ASSEMBLIES'); - bootConfig.aspnetCoreBrowserTools = bootConfigResponse.headers.get('ASPNETCORE-BROWSER-TOOLS'); - - return bootConfig; - - function defaultLoadBlazorBootJson(url: string): Promise { - return fetch(url, { - method: 'GET', - credentials: 'include', - cache: 'no-cache', - }); - } -} - async function importDotnetJs(startOptions: Partial): Promise { const browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate; if (!browserSupportsNativeWebAssembly) { From f0f22ef6c26f2db822c8b1ce59a29a8d42c8200f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 23 Jun 2023 14:03:17 +0200 Subject: [PATCH 03/14] Rewrite satellite and lazy assembly loading --- .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 63 +--------------- .../src/Hosting/WebAssemblyCultureProvider.cs | 18 +---- .../src/Services/LazyAssemblyLoader.cs | 73 ++++++------------- 3 files changed, 27 insertions(+), 127 deletions(-) diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 6c17e7cf4c8b..505ab854734f 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -5,9 +5,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable no-prototype-builtins */ import { DotNet } from '@microsoft/dotnet-js-interop'; -import { attachDebuggerHotkey, hasDebuggingEnabled } from './MonoDebugger'; +import { attachDebuggerHotkey } from './MonoDebugger'; import { showErrorNotification } from '../../BootErrors'; -import { WebAssemblyResourceLoader, LoadingResource } from '../WebAssemblyResourceLoader'; import { Platform, System_Array, Pointer, System_Object, System_String, HeapLock, PlatformApi } from '../Platform'; import { WebAssemblyBootResourceType, WebAssemblyStartOptions } from '../WebAssemblyStartOptions'; import { Blazor } from '../../GlobalExports'; @@ -235,8 +234,6 @@ async function createRuntimeInstance(options: Partial): attachDebuggerHotkey(resourceLoader); Blazor._internal.dotNetCriticalError = printErr; - Blazor._internal.loadLazyAssembly = (assemblyNameToLoad) => loadLazyAssembly(MONO_INTERNAL.resourceLoader, assemblyNameToLoad); - Blazor._internal.loadSatelliteAssemblies = (culturesToLoad, loader) => loadSatelliteAssemblies(resourceLoader, culturesToLoad, loader); setModuleImports('blazor-internal', { Blazor: { _internal: Blazor._internal }, }); @@ -267,55 +264,6 @@ const printErr = line => { showErrorNotification(); }; -async function loadSatelliteAssemblies(resourceLoader: WebAssemblyResourceLoader, culturesToLoad: string[], loader: (wrapper: { dll: Uint8Array }) => void): Promise { - const satelliteResources = resourceLoader.bootConfig.resources.satelliteResources; - if (!satelliteResources) { - return; - } - await Promise.all(culturesToLoad! - .filter(culture => satelliteResources.hasOwnProperty(culture)) - .map(culture => resourceLoader.loadResources(satelliteResources[culture], fileName => `_framework/${fileName}`, 'assembly')) - .reduce((previous, next) => previous.concat(next), new Array()) - .map(async resource => { - const response = await resource.response; - const bytes = await response.arrayBuffer(); - const wrapper = { dll: new Uint8Array(bytes) }; - loader(wrapper); - })); -} - -async function loadLazyAssembly(resourceLoader: WebAssemblyResourceLoader, assemblyNameToLoad: string): Promise<{ dll: Uint8Array, pdb: Uint8Array | null }> { - const resources = resourceLoader.bootConfig.resources; - const lazyAssemblies = resources.lazyAssembly; - if (!lazyAssemblies) { - throw new Error("No assemblies have been marked as lazy-loadable. Use the 'BlazorWebAssemblyLazyLoad' item group in your project file to enable lazy loading an assembly."); - } - - const assemblyMarkedAsLazy = lazyAssemblies.hasOwnProperty(assemblyNameToLoad); - if (!assemblyMarkedAsLazy) { - throw new Error(`${assemblyNameToLoad} must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading.`); - } - const dllNameToLoad = assemblyNameToLoad; - const pdbNameToLoad = changeExtension(assemblyNameToLoad, '.pdb'); - const shouldLoadPdb = hasDebuggingEnabled() && resources.pdb && lazyAssemblies.hasOwnProperty(pdbNameToLoad); - - const dllBytesPromise = resourceLoader.loadResource(dllNameToLoad, `_framework/${dllNameToLoad}`, lazyAssemblies[dllNameToLoad], 'assembly').response.then(response => response.arrayBuffer()); - if (shouldLoadPdb) { - const pdbBytesPromise = await resourceLoader.loadResource(pdbNameToLoad, `_framework/${pdbNameToLoad}`, lazyAssemblies[pdbNameToLoad], 'pdb').response.then(response => response.arrayBuffer()); - const [dllBytes, pdbBytes] = await Promise.all([dllBytesPromise, pdbBytesPromise]); - return { - dll: new Uint8Array(dllBytes), - pdb: new Uint8Array(pdbBytes), - }; - } else { - const dllBytes = await dllBytesPromise; - return { - dll: new Uint8Array(dllBytes), - pdb: null, - }; - } -} - function getArrayDataPointer(array: System_Array): number { return array + 12; // First byte from here is length, then following bytes are entries } @@ -358,15 +306,6 @@ function attachInteropInvoker(): void { }); } -function changeExtension(filename: string, newExtensionWithLeadingDot: string) { - const lastDotIndex = filename.lastIndexOf('.'); - if (lastDotIndex < 0) { - throw new Error(`No extension to replace in '${filename}'`); - } - - return filename.substr(0, lastDotIndex) + newExtensionWithLeadingDot; -} - function assertHeapIsNotLocked() { if (currentHeapLock) { throw new Error('Assertion failed - heap is currently locked'); diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs index dcf08f581bf5..060bd827b93f 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs @@ -4,8 +4,6 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices.JavaScript; -using System.Runtime.Loader; -using System.Runtime.Versioning; namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting; @@ -71,16 +69,7 @@ public virtual async ValueTask LoadCurrentCultureResourcesAsync() return; } - await WebAssemblyCultureProviderInterop.LoadSatelliteAssemblies(culturesToLoad, LoadSatelliteAssembly); - } - - [SupportedOSPlatform("browser")] - private void LoadSatelliteAssembly(JSObject wrapper) - { - var dllBytes = wrapper.GetPropertyAsByteArray("dll")!; - using var stream = new MemoryStream(dllBytes); - AssemblyLoadContext.Default.LoadFromStream(stream); - wrapper.Dispose(); + await WebAssemblyCultureProviderInterop.LoadSatelliteAssemblies(culturesToLoad); } internal static string[] GetCultures(CultureInfo cultureInfo) @@ -109,8 +98,7 @@ internal static string[] GetCultures(CultureInfo cultureInfo) private partial class WebAssemblyCultureProviderInterop { - [JSImport("Blazor._internal.loadSatelliteAssemblies", "blazor-internal")] - public static partial Task LoadSatelliteAssemblies(string[] culturesToLoad, - [JSMarshalAs>] Action assemblyLoader); + [JSImport("INTERNAL.loadSatelliteAssemblies")] + public static partial Task LoadSatelliteAssemblies(string[] culturesToLoad); } } diff --git a/src/Components/WebAssembly/WebAssembly/src/Services/LazyAssemblyLoader.cs b/src/Components/WebAssembly/WebAssembly/src/Services/LazyAssemblyLoader.cs index 3cff52948eb1..16a5ce5cb302 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Services/LazyAssemblyLoader.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Services/LazyAssemblyLoader.cs @@ -18,8 +18,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services; /// public sealed partial class LazyAssemblyLoader { - private HashSet? _loadedAssemblyCache; - /// /// Initializes a new instance of . /// @@ -68,67 +66,42 @@ private static Task> LoadAssembliesInServerAsync(IEnumerab [RequiresUnreferencedCode("Types and members the loaded assemblies depend on might be removed")] [SupportedOSPlatform("browser")] - private async Task> LoadAssembliesInClientAsync(IEnumerable assembliesToLoad) + private static async Task> LoadAssembliesInClientAsync(IEnumerable assembliesToLoad) { - if (_loadedAssemblyCache is null) - { - var loadedAssemblyCache = new HashSet(StringComparer.Ordinal); - var appDomainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - for (var i = 0; i < appDomainAssemblies.Length; i++) - { - var assembly = appDomainAssemblies[i]; - loadedAssemblyCache.Add(assembly.GetName().Name + ".dll"); - } + var newAssembliesToLoad = assembliesToLoad.ToList(); + var loadedAssemblies = new List(); + var pendingLoads = newAssembliesToLoad.Select(LazyAssemblyLoaderInterop.LoadLazyAssembly); - _loadedAssemblyCache = loadedAssemblyCache; - } + var loadedStatus = await Task.WhenAll(pendingLoads); + int i = 0; - // Check to see if the assembly has already been loaded and avoids reloading it if so. - // Note: in the future, as an extra precaution, we can call `Assembly.Load` and check - // to see if it throws FileNotFound to ensure that an assembly hasn't been loaded - // between when the cache of loaded assemblies was instantiated in the constructor - // and the invocation of this method. - var newAssembliesToLoad = new List(); - foreach (var assemblyToLoad in assembliesToLoad) + List? allAssemblies = null; + foreach (var loaded in loadedStatus) { - if (!_loadedAssemblyCache.Contains(assemblyToLoad)) + if (loaded) { - newAssembliesToLoad.Add(assemblyToLoad); + if (allAssemblies == null) + { + allAssemblies = AssemblyLoadContext.Default.Assemblies.ToList(); + } + + var assemblyName = Path.GetFileNameWithoutExtension(newAssembliesToLoad[i]); + var assembly = AssemblyLoadContext.Default.Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName); + if (assembly != null) + { + loadedAssemblies.Add(assembly); + } } - } - if (newAssembliesToLoad.Count == 0) - { - return Array.Empty(); + i++; } - var loadedAssemblies = new List(); - var pendingLoads = newAssembliesToLoad.Select(assemblyToLoad => LoadAssembly(assemblyToLoad, loadedAssemblies)); - - await Task.WhenAll(pendingLoads); return loadedAssemblies; } - [RequiresUnreferencedCode("Types and members the loaded assemblies depend on might be removed")] - [SupportedOSPlatform("browser")] - private async Task LoadAssembly(string assemblyToLoad, List loadedAssemblies) - { - using var files = await LazyAssemblyLoaderInterop.LoadLazyAssembly(assemblyToLoad); - - var dllBytes = files.GetPropertyAsByteArray("dll")!; - var pdbBytes = files.GetPropertyAsByteArray("pdb"); - Assembly loadedAssembly = pdbBytes == null - ? AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(dllBytes)) - : AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(dllBytes), new MemoryStream(pdbBytes)); - - loadedAssemblies.Add(loadedAssembly); - _loadedAssemblyCache!.Add(assemblyToLoad); - - } - private partial class LazyAssemblyLoaderInterop { - [JSImport("Blazor._internal.loadLazyAssembly", "blazor-internal")] - public static partial Task LoadLazyAssembly(string assemblyToLoad); + [JSImport("INTERNAL.loadLazyAssembly")] + public static partial Task LoadLazyAssembly(string assemblyToLoad); } } From 0b25ecf02f98679b7ec9217fe7445d4243d133fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 30 Jun 2023 14:35:03 +0200 Subject: [PATCH 04/14] Use debugLevel instead of debugBuild --- .../Web.JS/src/Platform/Mono/MonoDebugger.ts | 17 ++++++----------- .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 4 ++-- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoDebugger.ts b/src/Components/Web.JS/src/Platform/Mono/MonoDebugger.ts index 57cd33de460a..1c6798193376 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoDebugger.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoDebugger.ts @@ -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 { WebAssemblyResourceLoader } from '../WebAssemblyResourceLoader'; +import { MonoConfig } from 'dotnet'; const navigatorUA = navigator as MonoNavigatorUserAgent; const brands = navigatorUA.userAgentData && navigatorUA.userAgentData.brands; @@ -11,27 +11,22 @@ const currentBrowserIsChromeOrEdge = brands && brands.length > 0 : (window as any).chrome; const platform = navigatorUA.userAgentData?.platform ?? navigator.platform; -let hasReferencedPdbs = false; -let debugBuild = false; - -export function hasDebuggingEnabled(): boolean { - return (hasReferencedPdbs || debugBuild) && (currentBrowserIsChromeOrEdge || navigator.userAgent.includes('Firefox')); +function hasDebuggingEnabled(config: MonoConfig): boolean { + return config.debugLevel !== 0 && (currentBrowserIsChromeOrEdge || navigator.userAgent.includes('Firefox')); } -export function attachDebuggerHotkey(resourceLoader: WebAssemblyResourceLoader): void { - hasReferencedPdbs = !!resourceLoader.bootConfig.resources.pdb; - debugBuild = resourceLoader.bootConfig.debugBuild; +export function attachDebuggerHotkey(config: MonoConfig): void { // Use the combination shift+alt+D because it isn't used by the major browsers // for anything else by default const altKeyName = platform.match(/^Mac/i) ? 'Cmd' : 'Alt'; - if (hasDebuggingEnabled()) { + if (hasDebuggingEnabled(config)) { console.info(`Debugging hotkey: Shift+${altKeyName}+D (when application has focus)`); } // Even if debugging isn't enabled, we register the hotkey so we can report why it's not enabled document.addEventListener('keydown', evt => { if (evt.shiftKey && (evt.metaKey || evt.altKey) && evt.code === 'KeyD') { - if (!debugBuild && !hasReferencedPdbs) { + if (!hasDebuggingEnabled(config)) { console.error('Cannot start debugging, because the application was not compiled with debugging enabled.'); } else if (navigator.userAgent.includes('Firefox')) { launchFirefoxDebugger(); diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 505ab854734f..bdfad867fa42 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -223,7 +223,7 @@ async function createRuntimeInstance(options: Partial): anyDotnet.withStartupOptions(options).withModuleConfig(moduleConfig); runtime = await dotnet.create(); - const { MONO: mono, BINDING: binding, Module: module, setModuleImports, INTERNAL: mono_internal } = runtime; + const { MONO: mono, BINDING: binding, Module: module, setModuleImports, INTERNAL: mono_internal, getConfig } = runtime; Module = module; BINDING = binding; MONO = mono; @@ -231,7 +231,7 @@ async function createRuntimeInstance(options: Partial): const resourceLoader = MONO_INTERNAL.resourceLoader; platformApi.resourceLoader = resourceLoader; - attachDebuggerHotkey(resourceLoader); + attachDebuggerHotkey(getConfig()); Blazor._internal.dotNetCriticalError = printErr; setModuleImports('blazor-internal', { From e51036070f220790c0f66a656edd185b75d8f198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 30 Jun 2023 14:59:28 +0200 Subject: [PATCH 05/14] Remove WebAssemblyResourceLoader --- .../Web.JS/src/Boot.WebAssembly.Common.ts | 5 +---- .../src/JSInitializers/JSInitializers.ts | 2 +- .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 6 ++---- .../Web.JS/src/Platform/Platform.ts | 4 +--- .../src/Platform/WebAssemblyResourceLoader.ts | 19 ------------------- 5 files changed, 5 insertions(+), 31 deletions(-) delete mode 100644 src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts diff --git a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts index f6d14414898a..5897faedb83e 100644 --- a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts +++ b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts @@ -8,7 +8,6 @@ import * as Environment from './Environment'; import { BINDING, monoPlatform, dispatcher } from './Platform/Mono/MonoPlatform'; import { renderBatch, getRendererer, attachRootComponentToElement, attachRootComponentToLogicalElement } from './Rendering/Renderer'; import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch'; -import { WebAssemblyResourceLoader } from './Platform/WebAssemblyResourceLoader'; import { Pointer } from './Platform/Platform'; import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions'; import { addDispatchEventMiddleware } from './Rendering/WebRendererInteropMethods'; @@ -107,18 +106,16 @@ export async function startWebAssembly(options?: Partial Promise; export type AfterBlazorStartedCallback = (blazor: typeof Blazor) => Promise; diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index bdfad867fa42..35af46699554 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -58,9 +58,9 @@ export const monoPlatform: Platform = { return createRuntimeInstance(options); }, - callEntryPoint: async function callEntryPoint(assemblyName: string): Promise { + callEntryPoint: async function callEntryPoint(): Promise { try { - await runtime.runMain(assemblyName, []); + await runtime.runMain(runtime.getConfig().mainAssemblyName!, []); } catch (error) { console.error(error); showErrorNotification(); @@ -228,8 +228,6 @@ async function createRuntimeInstance(options: Partial): BINDING = binding; MONO = mono; MONO_INTERNAL = mono_internal; - const resourceLoader = MONO_INTERNAL.resourceLoader; - platformApi.resourceLoader = resourceLoader; attachDebuggerHotkey(getConfig()); diff --git a/src/Components/Web.JS/src/Platform/Platform.ts b/src/Components/Web.JS/src/Platform/Platform.ts index 10f42ff15a1f..20a46499b6a2 100644 --- a/src/Components/Web.JS/src/Platform/Platform.ts +++ b/src/Components/Web.JS/src/Platform/Platform.ts @@ -3,13 +3,12 @@ import { MonoObject, MonoString, MonoArray } from 'dotnet/dotnet-legacy'; import { WebAssemblyStartOptions } from './WebAssemblyStartOptions'; -import { WebAssemblyResourceLoader } from './WebAssemblyResourceLoader'; import { JSInitializer } from '../JSInitializers/JSInitializers'; export interface Platform { start(options: Partial): Promise; - callEntryPoint(assemblyName: string): Promise; + callEntryPoint(): Promise; toUint8Array(array: System_Array): Uint8Array; @@ -30,7 +29,6 @@ export interface Platform { } export type PlatformApi = { - resourceLoader: WebAssemblyResourceLoader, jsInitializer: JSInitializer } diff --git a/src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts b/src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts deleted file mode 100644 index ca9d588a30ad..000000000000 --- a/src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { BootJsonData } from 'dotnet'; -import { WebAssemblyBootResourceType } from './WebAssemblyStartOptions'; - -export interface WebAssemblyResourceLoader { - readonly bootConfig: BootJsonData; - loadResources(resources: ResourceList, url: (name: string) => string, resourceType: WebAssemblyBootResourceType): LoadingResource[]; - loadResource(name: string, url: string, contentHash: string, resourceType: WebAssemblyBootResourceType): LoadingResource; -} - -export type ResourceList = { [name: string]: string }; - -export interface LoadingResource { - name: string; - url: string; - response: Promise; -} From 5c55ff7c5cbcbc76636387714abb2bfefdc28d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Mon, 3 Jul 2023 14:03:23 +0200 Subject: [PATCH 06/14] Move env:__ASPNETCORE_BROWSER_TOOLS to runtime --- src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 35af46699554..fe456a6fd962 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -189,11 +189,6 @@ function prepareRuntimeConfig(options: Partial, platfor bootConfig.environmentVariables['__BLAZOR_SHARDED_ICU'] = '1'; } - if (bootConfig.aspnetCoreBrowserTools) { - // See https://github.com/dotnet/aspnetcore/issues/37357#issuecomment-941237000 - bootConfig.environmentVariables['__ASPNETCORE_BROWSER_TOOLS'] = bootConfig.aspnetCoreBrowserTools; - } - Blazor._internal.getApplicationEnvironment = () => bootConfig.applicationEnvironment!; platformApi.jsInitializer = await fetchAndInvokeInitializers(bootConfig, options); From 589a0bc8ca4ae9b29e8e40e616a1640e2a71f4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Mon, 3 Jul 2023 15:10:51 +0200 Subject: [PATCH 07/14] Use JS initializers downloaded by runtime --- .../JSInitializers.WebAssembly.ts | 32 +++++++++++++++++-- .../src/JSInitializers/JSInitializers.ts | 6 +++- .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 10 +++--- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts index 0d97deb1ff5d..e1a0069bc1b8 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts @@ -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 { BootJsonData } from 'dotnet'; +import { BootJsonData, MonoConfig } from 'dotnet'; import { WebAssemblyStartOptions } from '../Platform/WebAssemblyStartOptions'; -import { JSInitializer } from './JSInitializers'; +import { BlazorInitializer, JSInitializer } from './JSInitializers'; export async function fetchAndInvokeInitializers(bootConfig: BootJsonData, options: Partial): Promise { const initializers = bootConfig.resources.libraryInitializers; @@ -17,3 +17,31 @@ export async function fetchAndInvokeInitializers(bootConfig: BootJsonData, optio return jsInitializer; } + +export async function invokeOnBeforeStart(moduleConfig: MonoConfig, bootConfig: BootJsonData, options: Partial): Promise { + const initializerArguments = [options, bootConfig.resources.extensions]; + const jsInitializer = new JSInitializer(); + + const beforeStartPromises: Promise[] = []; + if (moduleConfig.libraryInitializers) { + for (let i = 0; i < moduleConfig.libraryInitializers.length; i++) { + const initializer = moduleConfig.libraryInitializers[i]; + const blazorInitializer = initializer as Partial; + if (blazorInitializer === undefined) { + continue; + } + + const { beforeStart: beforeStart, afterStarted: afterStarted } = blazorInitializer; + if (afterStarted) { + jsInitializer.addAfterStartedCallback(afterStarted); + } + + if (beforeStart) { + beforeStartPromises.push(beforeStart(...initializerArguments)); + } + } + } + + await Promise.all(beforeStartPromises); + return jsInitializer; +} diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts index 0dc8531dd6e8..b4e31ddad937 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts @@ -6,11 +6,15 @@ import { rendererAttached } from '../Rendering/WebRendererInteropMethods'; type BeforeBlazorStartedCallback = (...args: unknown[]) => Promise; export type AfterBlazorStartedCallback = (blazor: typeof Blazor) => Promise; -type BlazorInitializer = { beforeStart: BeforeBlazorStartedCallback, afterStarted: AfterBlazorStartedCallback }; +export type BlazorInitializer = { beforeStart: BeforeBlazorStartedCallback, afterStarted: AfterBlazorStartedCallback }; export class JSInitializer { private afterStartedCallbacks: AfterBlazorStartedCallback[] = []; + addAfterStartedCallback(afterStartedCallback: AfterBlazorStartedCallback): void { + this.afterStartedCallbacks.push(afterStartedCallback); + } + async importInitializersAsync(initializerFiles: string[], initializerArguments: unknown[]): Promise { await Promise.all(initializerFiles.map(f => importAndInvokeInitializer(this, f))); diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index fe456a6fd962..9db3b7cc0972 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -12,7 +12,7 @@ import { WebAssemblyBootResourceType, WebAssemblyStartOptions } from '../WebAsse import { Blazor } from '../../GlobalExports'; import { DotnetModuleConfig, EmscriptenModule, MonoConfig, ModuleAPI, BootJsonData, ICUDataMode, RuntimeAPI } from 'dotnet'; import { BINDINGType, MONOType } from 'dotnet/dotnet-legacy'; -import { fetchAndInvokeInitializers } from '../../JSInitializers/JSInitializers.WebAssembly'; +import { invokeOnBeforeStart } from '../../JSInitializers/JSInitializers.WebAssembly'; // initially undefined and only fully initialized after createEmscriptenModuleInstance() export let BINDING: BINDINGType = undefined as any; @@ -27,8 +27,6 @@ const maxSafeNumberHighPart = Math.pow(2, 21) - 1; // The high-order int32 from let currentHeapLock: MonoHeapLock | null = null; -let applicationEnvironment = 'Production'; - // Memory access helpers // The implementations are exactly equivalent to what the global getValue(addr, type) function does, // except without having to parse the 'type' parameter, and with less risk of mistakes at the call site @@ -177,10 +175,10 @@ function prepareRuntimeConfig(options: Partial, platfor const config: MonoConfig = { maxParallelDownloads: 1000000, // disable throttling parallel downloads enableDownloadRetry: false, // disable retry downloads - applicationEnvironment: applicationEnvironment, + applicationEnvironment: options.environment, }; - const onConfigLoaded = async (bootConfig: BootJsonData & MonoConfig): Promise => { + const onConfigLoaded = async (bootConfig: MonoConfig & BootJsonData): Promise => { if (!bootConfig.environmentVariables) { bootConfig.environmentVariables = {}; } @@ -191,7 +189,7 @@ function prepareRuntimeConfig(options: Partial, platfor Blazor._internal.getApplicationEnvironment = () => bootConfig.applicationEnvironment!; - platformApi.jsInitializer = await fetchAndInvokeInitializers(bootConfig, options); + platformApi.jsInitializer = await invokeOnBeforeStart(bootConfig, bootConfig, options); }; const moduleConfig = (window['Module'] || {}) as typeof Module; From b6074ae250ce2b4ef2a2aa2fdfbaac9cd6ac9b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Mon, 3 Jul 2023 18:05:17 +0200 Subject: [PATCH 08/14] Update dotnet.d.ts --- src/Components/Web.JS/@types/dotnet/dotnet.d.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts index 3c35d593e671..88d700efe615 100644 --- a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts +++ b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts @@ -3,7 +3,7 @@ //! //! This is generated file, see src/mono/wasm/runtime/rollup.config.js -//! This is not considered public API with backward compatibility guarantees. +//! This is not considered public API with backward compatibility guarantees. interface DotnetHostBuilder { withConfig(config: MonoConfig): DotnetHostBuilder; @@ -140,6 +140,10 @@ type MonoConfig = { * application environment */ applicationEnvironment?: string; + /** + * exports from registered javascript library modules + */ + libraryInitializers?: any[]; }; interface ResourceRequest { name: string; @@ -182,8 +186,8 @@ interface AssetEntry extends ResourceRequest { } type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads"; type GlobalizationMode = "icu" | // load ICU globalization data from any runtime assets with behavior "icu". -"invariant" | // operate in invariant globalization mode. -"auto"; + "invariant" | // operate in invariant globalization mode. + "auto"; type DotnetModuleConfig = { disableDotnet6Compatibility?: boolean; config?: MonoConfig; From 339392e3544c3a4907fbb08217bcf3adac595947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 4 Jul 2023 11:37:17 +0200 Subject: [PATCH 09/14] Drop rest of use of BootJsonData --- .../Web.JS/@types/dotnet/dotnet.d.ts | 20 +++++++++++++++---- .../JSInitializers.WebAssembly.ts | 19 +++--------------- .../src/JSInitializers/JSInitializers.ts | 2 ++ .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 16 +++++++-------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts index 88d700efe615..3ef22565b49a 100644 --- a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts +++ b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts @@ -141,9 +141,17 @@ type MonoConfig = { */ applicationEnvironment?: string; /** - * exports from registered javascript library modules + * exports from library es6 modules + * + * nuget packages can contain wwwroot/*.lib.module.js which are treated as es6 modules + * runtime calls 'onRuntimeReady' and pass in runtime API + * blazor calls 'beforeStart' and 'afterStarted' */ libraryInitializers?: any[]; + /** + * A definition of assets to load along with the runtime. + */ + resources: ResourceGroups; }; interface ResourceRequest { name: string; @@ -185,9 +193,13 @@ interface AssetEntry extends ResourceRequest { pendingDownload?: LoadingResource; } type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads"; -type GlobalizationMode = "icu" | // load ICU globalization data from any runtime assets with behavior "icu". - "invariant" | // operate in invariant globalization mode. - "auto"; +export const enum GlobalizationMode { + Sharded = "sharded", // load sharded ICU data + All = "all", // load all ICU data + Invariant = "invariant", // operate in invariant globalization mode. + Custom = "custom", // use user defined icu file + Hybrid = "hybrid" // operate in hybrid globalization mode with small ICU files, using native platform functions +} type DotnetModuleConfig = { disableDotnet6Compatibility?: boolean; config?: MonoConfig; diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts index e1a0069bc1b8..039f24e66071 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts @@ -1,25 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { BootJsonData, MonoConfig } from 'dotnet'; +import { MonoConfig } from 'dotnet'; import { WebAssemblyStartOptions } from '../Platform/WebAssemblyStartOptions'; import { BlazorInitializer, JSInitializer } from './JSInitializers'; -export async function fetchAndInvokeInitializers(bootConfig: BootJsonData, options: Partial): Promise { - const initializers = bootConfig.resources.libraryInitializers; - const jsInitializer = new JSInitializer(); - if (initializers) { - await jsInitializer.importInitializersAsync( - Object.keys(initializers), - [options, bootConfig.resources.extensions] - ); - } - - return jsInitializer; -} - -export async function invokeOnBeforeStart(moduleConfig: MonoConfig, bootConfig: BootJsonData, options: Partial): Promise { - const initializerArguments = [options, bootConfig.resources.extensions]; +export async function invokeOnBeforeStart(moduleConfig: MonoConfig, options: Partial): Promise { + const initializerArguments = [options, moduleConfig.resources?.extensions ?? {}]; const jsInitializer = new JSInitializer(); const beforeStartPromises: Promise[] = []; diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts index b4e31ddad937..f6fd9161b2dd 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts @@ -16,6 +16,8 @@ export class JSInitializer { } async importInitializersAsync(initializerFiles: string[], initializerArguments: unknown[]): Promise { + // This code is not called on WASM, because library intializers are imported by runtime. + await Promise.all(initializerFiles.map(f => importAndInvokeInitializer(this, f))); function adjustPath(path: string): string { diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 9db3b7cc0972..59769ec2f6b2 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -10,7 +10,7 @@ import { showErrorNotification } from '../../BootErrors'; import { Platform, System_Array, Pointer, System_Object, System_String, HeapLock, PlatformApi } from '../Platform'; import { WebAssemblyBootResourceType, WebAssemblyStartOptions } from '../WebAssemblyStartOptions'; import { Blazor } from '../../GlobalExports'; -import { DotnetModuleConfig, EmscriptenModule, MonoConfig, ModuleAPI, BootJsonData, ICUDataMode, RuntimeAPI } from 'dotnet'; +import { DotnetModuleConfig, EmscriptenModule, MonoConfig, ModuleAPI, BootJsonData, ICUDataMode, RuntimeAPI, GlobalizationMode } from 'dotnet'; import { BINDINGType, MONOType } from 'dotnet/dotnet-legacy'; import { invokeOnBeforeStart } from '../../JSInitializers/JSInitializers.WebAssembly'; @@ -178,18 +178,18 @@ function prepareRuntimeConfig(options: Partial, platfor applicationEnvironment: options.environment, }; - const onConfigLoaded = async (bootConfig: MonoConfig & BootJsonData): Promise => { - if (!bootConfig.environmentVariables) { - bootConfig.environmentVariables = {}; + const onConfigLoaded = async (loadedConfig: MonoConfig): Promise => { + if (!loadedConfig.environmentVariables) { + loadedConfig.environmentVariables = {}; } - if (bootConfig.icuDataMode === ICUDataMode.Sharded) { - bootConfig.environmentVariables['__BLAZOR_SHARDED_ICU'] = '1'; + if (loadedConfig.globalizationMode === GlobalizationMode.Sharded) { + loadedConfig.environmentVariables['__BLAZOR_SHARDED_ICU'] = '1'; } - Blazor._internal.getApplicationEnvironment = () => bootConfig.applicationEnvironment!; + Blazor._internal.getApplicationEnvironment = () => loadedConfig.applicationEnvironment!; - platformApi.jsInitializer = await invokeOnBeforeStart(bootConfig, bootConfig, options); + platformApi.jsInitializer = await invokeOnBeforeStart(loadedConfig, options); }; const moduleConfig = (window['Module'] || {}) as typeof Module; From b7091a9c7b1da7111750d4ad6e8b64d59df2313f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 4 Jul 2023 11:45:49 +0200 Subject: [PATCH 10/14] Include monoConfig.extensions --- src/Components/Web.JS/@types/dotnet/dotnet.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts index 3ef22565b49a..2c2d6676348e 100644 --- a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts +++ b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts @@ -152,6 +152,10 @@ type MonoConfig = { * A definition of assets to load along with the runtime. */ resources: ResourceGroups; + /** + * config extensions declared in MSBuild items @(WasmBootConfigExtension) + */ + extensions?: { [name: string]: any }; }; interface ResourceRequest { name: string; From b7ffc30db5ab6b00c8abed96c03f87af5ab3bcbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 4 Jul 2023 11:49:36 +0200 Subject: [PATCH 11/14] Use generated dotnet.d.ts from runtime --- .../Web.JS/@types/dotnet/dotnet.d.ts | 263 ++++++++++++------ 1 file changed, 176 insertions(+), 87 deletions(-) diff --git a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts index 2c2d6676348e..4c06a952bbb8 100644 --- a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts +++ b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts @@ -5,23 +5,6 @@ //! This is not considered public API with backward compatibility guarantees. -interface DotnetHostBuilder { - withConfig(config: MonoConfig): DotnetHostBuilder; - withConfigSrc(configSrc: string): DotnetHostBuilder; - withApplicationArguments(...args: string[]): DotnetHostBuilder; - withEnvironmentVariable(name: string, value: string): DotnetHostBuilder; - withEnvironmentVariables(variables: { - [i: string]: string; - }): DotnetHostBuilder; - withVirtualWorkingDirectory(vfsPath: string): DotnetHostBuilder; - withDiagnosticTracing(enabled: boolean): DotnetHostBuilder; - withDebugging(level: number): DotnetHostBuilder; - withMainAssembly(mainAssemblyName: string): DotnetHostBuilder; - withApplicationArgumentsFromQuery(): DotnetHostBuilder; - create(): Promise; - run(): Promise; -} - declare interface NativePointer { __brandNativePointer: "NativePointer"; } @@ -35,18 +18,28 @@ declare interface Int32Ptr extends NativePointer { __brand: "Int32Ptr"; } declare interface EmscriptenModule { + /** @deprecated Please use growableHeapI8() instead.*/ HEAP8: Int8Array; + /** @deprecated Please use growableHeapI16() instead.*/ HEAP16: Int16Array; + /** @deprecated Please use growableHeapI32() instead. */ HEAP32: Int32Array; + /** @deprecated Please use growableHeapI64() instead. */ + HEAP64: BigInt64Array; + /** @deprecated Please use growableHeapU8() instead. */ HEAPU8: Uint8Array; + /** @deprecated Please use growableHeapU16() instead. */ HEAPU16: Uint16Array; + /** @deprecated Please use growableHeapU32() instead */ HEAPU32: Uint32Array; + /** @deprecated Please use growableHeapF32() instead */ HEAPF32: Float32Array; + /** @deprecated Please use growableHeapF64() instead. */ HEAPF64: Float64Array; _malloc(size: number): VoidPtr; _free(ptr: VoidPtr): void; - print(message: string): void; - printErr(message: string): void; + out(message: string): void; + err(message: string): void; ccall(ident: string, returnType?: string | null, argTypes?: string[], args?: any[], opts?: any): T; cwrap(ident: string, returnType: string, argTypes?: string[], opts?: any): T; cwrap(ident: string, ...args: any[]): T; @@ -55,17 +48,13 @@ declare interface EmscriptenModule { getValue(ptr: number, type: string, noSafe?: number | boolean): number; UTF8ToString(ptr: CharPtr, maxBytesToRead?: number): string; UTF8ArrayToString(u8Array: Uint8Array, idx?: number, maxBytesToRead?: number): string; + stringToUTF8Array(str: string, heap: Uint8Array, outIdx: number, maxBytesToWrite: number): void; FS_createPath(parent: string, path: string, canRead?: boolean, canWrite?: boolean): string; FS_createDataFile(parent: string, name: string, data: TypedArray, canRead: boolean, canWrite: boolean, canOwn?: boolean): string; - FS_readFile(filename: string, opts: any): any; - removeRunDependency(id: string): void; - addRunDependency(id: string): void; addFunction(fn: Function, signature: string): number; - getWasmTableEntry(index: number): any; stackSave(): VoidPtr; stackRestore(stack: VoidPtr): void; stackAlloc(size: number): VoidPtr; - ready: Promise; instantiateWasm?: InstantiateWasmCallBack; preInit?: (() => any)[] | (() => any); preRun?: (() => any)[] | (() => any); @@ -79,6 +68,25 @@ type InstantiateWasmSuccessCallback = (instance: WebAssembly.Instance, module: W type InstantiateWasmCallBack = (imports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback) => any; declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; +interface DotnetHostBuilder { + withConfig(config: MonoConfig): DotnetHostBuilder; + withConfigSrc(configSrc: string): DotnetHostBuilder; + withApplicationArguments(...args: string[]): DotnetHostBuilder; + withEnvironmentVariable(name: string, value: string): DotnetHostBuilder; + withEnvironmentVariables(variables: { + [i: string]: string; + }): DotnetHostBuilder; + withVirtualWorkingDirectory(vfsPath: string): DotnetHostBuilder; + withDiagnosticTracing(enabled: boolean): DotnetHostBuilder; + withDebugging(level: number): DotnetHostBuilder; + withMainAssembly(mainAssemblyName: string): DotnetHostBuilder; + withApplicationArgumentsFromQuery(): DotnetHostBuilder; + withApplicationEnvironment(applicationEnvironment?: string): DotnetHostBuilder; + withApplicationCulture(applicationCulture?: string): DotnetHostBuilder; + withResourceLoader(loadBootResource?: LoadBootResourceCallback): DotnetHostBuilder; + create(): Promise; + run(): Promise; +} type MonoConfig = { /** * The subfolder containing managed assemblies and pdbs. This is relative to dotnet.js script. @@ -115,7 +123,7 @@ type MonoConfig = { /** * debugLevel > 0 enables debugging and sets the debug log level to debugLevel * debugLevel == 0 disables debugging and enables interpreter optimizations - * debugLevel < 0 enabled debugging and disables debug logging. + * debugLevel < 0 enables debugging and disables debug logging. */ debugLevel?: number; /** @@ -136,10 +144,32 @@ type MonoConfig = { * If true, the snapshot of runtime's memory will be stored in the browser and used for faster startup next time. Default is false. */ startupMemoryCache?: boolean; + /** + * hash of assets + */ + assetsHash?: string; /** * application environment */ applicationEnvironment?: string; + /** + * query string to be used for asset loading + */ + assetUniqueQuery?: string; + /** + * Gets the application culture. This is a name specified in the BCP 47 format. See https://tools.ietf.org/html/bcp47 + */ + applicationCulture?: string; + /** + * Overrides the built-in boot resource loading mechanism so that boot resources can be fetched + * from a custom source, such as an external CDN. + * @param type The type of the resource to be loaded. + * @param name The name of the resource to be loaded. + * @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. + */ + loadBootResource?: LoadBootResourceCallback; /** * exports from library es6 modules * @@ -149,14 +179,47 @@ type MonoConfig = { */ libraryInitializers?: any[]; /** - * A definition of assets to load along with the runtime. + * definition of assets to load along with the runtime. */ - resources: ResourceGroups; + resources?: ResourceGroups$1; /** * config extensions declared in MSBuild items @(WasmBootConfigExtension) */ - extensions?: { [name: string]: any }; + extensions?: { + [name: string]: any; + }; +}; +type ResourceExtensions = { + [extensionName: string]: ResourceList$1; }; +interface ResourceGroups$1 { + readonly hash?: string; + readonly assembly?: ResourceList$1; + readonly lazyAssembly?: ResourceList$1; + readonly pdb?: ResourceList$1; + readonly runtime?: ResourceList$1; + readonly satelliteResources?: { + [cultureName: string]: ResourceList$1; + }; + readonly libraryInitializers?: ResourceList$1; + readonly extensions?: ResourceExtensions; + readonly vfs?: { + [virtualPath: string]: ResourceList$1; + }; +} +type ResourceList$1 = { + [name: string]: string; +}; +/** + * Overrides the built-in boot resource loading mechanism so that boot resources can be fetched + * from a custom source, such as an external CDN. + * @param type The type of the resource to be loaded. + * @param name The name of the resource to be loaded. + * @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. + */ +type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) => string | Promise | null | undefined; interface ResourceRequest { name: string; behavior: AssetBehaviours; @@ -196,19 +259,19 @@ interface AssetEntry extends ResourceRequest { */ pendingDownload?: LoadingResource; } -type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads"; -export const enum GlobalizationMode { - Sharded = "sharded", // load sharded ICU data - All = "all", // load all ICU data - Invariant = "invariant", // operate in invariant globalization mode. - Custom = "custom", // use user defined icu file - Hybrid = "hybrid" // operate in hybrid globalization mode with small ICU files, using native platform functions +type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads" | "js-module-runtime" | "js-module-dotnet" | "js-module-native" | "symbols"; +declare const enum GlobalizationMode { + Sharded = "sharded", + All = "all", + Invariant = "invariant", + Custom = "custom", + Hybrid = "hybrid" } type DotnetModuleConfig = { disableDotnet6Compatibility?: boolean; config?: MonoConfig; configSrc?: string; - onConfigLoaded?: (config: MonoConfig & BootJsonData) => void | Promise; + onConfigLoaded?: (config: MonoConfig) => void | Promise; onDotnetReady?: () => void | Promise; onDownloadResourceProgress?: (resourcesLoaded: number, totalResources: number) => void; getApplicationEnvironment?: (bootConfigResponse: Response) => string | null; @@ -247,6 +310,15 @@ type APIType = { getHeapI64Big: (offset: NativePointer) => bigint; getHeapF32: (offset: NativePointer) => number; getHeapF64: (offset: NativePointer) => number; + localHeapViewI8: () => Int8Array; + localHeapViewI16: () => Int16Array; + localHeapViewI32: () => Int32Array; + localHeapViewI64Big: () => BigInt64Array; + localHeapViewU8: () => Uint8Array; + localHeapViewU16: () => Uint16Array; + localHeapViewU32: () => Uint32Array; + localHeapViewF32: () => Float32Array; + localHeapViewF64: () => Float64Array; }; type RuntimeAPI = { /** @@ -270,8 +342,67 @@ type ModuleAPI = { dotnet: DotnetHostBuilder; exit: (code: number, reason?: any) => void; }; -declare function createDotnetRuntime(moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)): Promise; -type CreateDotnetRuntimeType = typeof createDotnetRuntime; +type CreateDotnetRuntimeType = (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) => Promise; +type WebAssemblyBootResourceType = "assembly" | "pdb" | "dotnetjs" | "dotnetwasm" | "globalization" | "manifest" | "configuration"; + +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 debugLevel: number; + readonly linkerEnabled: boolean; + readonly cacheBootResources: boolean; + readonly config: string[]; + readonly icuDataMode: ICUDataMode; + readonly startupMemoryCache: boolean | undefined; + readonly runtimeOptions: string[] | undefined; + readonly environmentVariables?: { + [name: string]: string; + }; + readonly diagnosticTracing?: boolean; + readonly pthreadPoolSize: number; + modifiableAssemblies: string | null; + aspnetCoreBrowserTools: string | null; + readonly extensions?: { + [name: string]: any; + }; +} +type BootJsonDataExtension = { + [extensionName: string]: ResourceList; +}; +interface ResourceGroups { + readonly hash?: string; + 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; + readonly vfs?: { + [virtualPath: string]: ResourceList; + }; +} +type ResourceList = { + [name: string]: string; +}; +type ExtendedResourceList = { + [name: string]: { + hash: string; + behavior: string; + }; +}; +declare enum ICUDataMode { + Sharded = 0, + All = 1, + Invariant = 2, + Custom = 3, + Hybrid = 4 +} interface IDisposable { dispose(): void; @@ -297,56 +428,14 @@ interface IMemoryView extends IDisposable { get byteLength(): number; } -declare global { - function getDotnetRuntime(runtimeId: number): RuntimeAPI | undefined; -} +declare function mono_exit(exit_code: number, reason?: any): void; -declare const dotnet: ModuleAPI["dotnet"]; -declare const exit: ModuleAPI["exit"]; +declare const dotnet: DotnetHostBuilder; +declare const exit: typeof mono_exit; -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; -} - -type BootJsonDataExtension = { [extensionName: string]: ResourceList }; - -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; -} - -type ResourceList = { [name: string]: string }; -type ExtendedResourceList = { - [name: string]: { - hash: string, - behavior: string - } -}; - -declare const enum ICUDataMode { - Sharded, - All, - Invariant, - Custom +declare global { + function getDotnetRuntime(runtimeId: number): RuntimeAPI | undefined; } +declare const createDotnetRuntime: CreateDotnetRuntimeType; -export { AssetEntry, CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, BootJsonData, ICUDataMode, createDotnetRuntime as default, dotnet, exit }; +export { AssetEntry, BootJsonData, CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, ICUDataMode, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit }; From 89386262dfd959173f6120c7e17c76a864bba7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 7 Jul 2023 10:26:15 +0200 Subject: [PATCH 12/14] Clean up --- .../Web.JS/@types/dotnet/dotnet.d.ts | 85 +++---------------- .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 2 +- 2 files changed, 14 insertions(+), 73 deletions(-) diff --git a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts index 4c06a952bbb8..79a3ffc1dfbc 100644 --- a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts +++ b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts @@ -174,14 +174,14 @@ type MonoConfig = { * exports from library es6 modules * * nuget packages can contain wwwroot/*.lib.module.js which are treated as es6 modules - * runtime calls 'onRuntimeReady' and pass in runtime API + * runtime calls 'onRuntimeConfigLoaded(config: MonoConfig)' and 'onRuntimeReady(api: RuntimeAPI)' * blazor calls 'beforeStart' and 'afterStarted' */ libraryInitializers?: any[]; /** * definition of assets to load along with the runtime. */ - resources?: ResourceGroups$1; + resources?: ResourceGroups; /** * config extensions declared in MSBuild items @(WasmBootConfigExtension) */ @@ -190,24 +190,24 @@ type MonoConfig = { }; }; type ResourceExtensions = { - [extensionName: string]: ResourceList$1; + [extensionName: string]: ResourceList; }; -interface ResourceGroups$1 { +interface ResourceGroups { readonly hash?: string; - readonly assembly?: ResourceList$1; - readonly lazyAssembly?: ResourceList$1; - readonly pdb?: ResourceList$1; - readonly runtime?: ResourceList$1; + readonly assembly?: ResourceList; + readonly lazyAssembly?: ResourceList; + readonly pdb?: ResourceList; + readonly runtime?: ResourceList; readonly satelliteResources?: { - [cultureName: string]: ResourceList$1; + [cultureName: string]: ResourceList; }; - readonly libraryInitializers?: ResourceList$1; + readonly libraryInitializers?: ResourceList; readonly extensions?: ResourceExtensions; readonly vfs?: { - [virtualPath: string]: ResourceList$1; + [virtualPath: string]: ResourceList; }; } -type ResourceList$1 = { +type ResourceList = { [name: string]: string; }; /** @@ -345,65 +345,6 @@ type ModuleAPI = { type CreateDotnetRuntimeType = (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) => Promise; type WebAssemblyBootResourceType = "assembly" | "pdb" | "dotnetjs" | "dotnetwasm" | "globalization" | "manifest" | "configuration"; -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 debugLevel: number; - readonly linkerEnabled: boolean; - readonly cacheBootResources: boolean; - readonly config: string[]; - readonly icuDataMode: ICUDataMode; - readonly startupMemoryCache: boolean | undefined; - readonly runtimeOptions: string[] | undefined; - readonly environmentVariables?: { - [name: string]: string; - }; - readonly diagnosticTracing?: boolean; - readonly pthreadPoolSize: number; - modifiableAssemblies: string | null; - aspnetCoreBrowserTools: string | null; - readonly extensions?: { - [name: string]: any; - }; -} -type BootJsonDataExtension = { - [extensionName: string]: ResourceList; -}; -interface ResourceGroups { - readonly hash?: string; - 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; - readonly vfs?: { - [virtualPath: string]: ResourceList; - }; -} -type ResourceList = { - [name: string]: string; -}; -type ExtendedResourceList = { - [name: string]: { - hash: string; - behavior: string; - }; -}; -declare enum ICUDataMode { - Sharded = 0, - All = 1, - Invariant = 2, - Custom = 3, - Hybrid = 4 -} - interface IDisposable { dispose(): void; get isDisposed(): boolean; @@ -438,4 +379,4 @@ declare global { } declare const createDotnetRuntime: CreateDotnetRuntimeType; -export { AssetEntry, BootJsonData, CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, ICUDataMode, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit }; +export { AssetEntry, CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit }; diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 59769ec2f6b2..88ba6800eba7 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -10,7 +10,7 @@ import { showErrorNotification } from '../../BootErrors'; import { Platform, System_Array, Pointer, System_Object, System_String, HeapLock, PlatformApi } from '../Platform'; import { WebAssemblyBootResourceType, WebAssemblyStartOptions } from '../WebAssemblyStartOptions'; import { Blazor } from '../../GlobalExports'; -import { DotnetModuleConfig, EmscriptenModule, MonoConfig, ModuleAPI, BootJsonData, ICUDataMode, RuntimeAPI, GlobalizationMode } from 'dotnet'; +import { DotnetModuleConfig, EmscriptenModule, MonoConfig, ModuleAPI, RuntimeAPI, GlobalizationMode } from 'dotnet'; import { BINDINGType, MONOType } from 'dotnet/dotnet-legacy'; import { invokeOnBeforeStart } from '../../JSInitializers/JSInitializers.WebAssembly'; From 97223007dda8a36002ea6dfd0be91e9447e956ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 12 Jul 2023 14:27:01 +0200 Subject: [PATCH 13/14] Use invokeLibraryInitializers. Update dotnet.d.ts. Rename functions to match newer emscripten --- .../Web.JS/@types/dotnet/dotnet.d.ts | 103 +++++++++++++----- .../Web.JS/src/Boot.WebAssembly.Common.ts | 9 +- src/Components/Web.JS/src/Boot.WebAssembly.ts | 4 +- .../JSInitializers.WebAssembly.ts | 34 ------ .../src/JSInitializers/JSInitializers.ts | 4 - .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 23 ++-- .../Web.JS/src/Platform/Platform.ts | 3 +- 7 files changed, 97 insertions(+), 83 deletions(-) delete mode 100644 src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts diff --git a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts index 79a3ffc1dfbc..2d0681bedfdf 100644 --- a/src/Components/Web.JS/@types/dotnet/dotnet.d.ts +++ b/src/Components/Web.JS/@types/dotnet/dotnet.d.ts @@ -83,6 +83,10 @@ interface DotnetHostBuilder { withApplicationArgumentsFromQuery(): DotnetHostBuilder; withApplicationEnvironment(applicationEnvironment?: string): DotnetHostBuilder; withApplicationCulture(applicationCulture?: string): DotnetHostBuilder; + /** + * Overrides the built-in boot resource loading mechanism so that boot resources can be fetched + * from a custom source, such as an external CDN. + */ withResourceLoader(loadBootResource?: LoadBootResourceCallback): DotnetHostBuilder; create(): Promise; run(): Promise; @@ -152,32 +156,10 @@ type MonoConfig = { * application environment */ applicationEnvironment?: string; - /** - * query string to be used for asset loading - */ - assetUniqueQuery?: string; /** * Gets the application culture. This is a name specified in the BCP 47 format. See https://tools.ietf.org/html/bcp47 */ applicationCulture?: string; - /** - * Overrides the built-in boot resource loading mechanism so that boot resources can be fetched - * from a custom source, such as an external CDN. - * @param type The type of the resource to be loaded. - * @param name The name of the resource to be loaded. - * @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. - */ - loadBootResource?: LoadBootResourceCallback; - /** - * exports from library es6 modules - * - * nuget packages can contain wwwroot/*.lib.module.js which are treated as es6 modules - * runtime calls 'onRuntimeConfigLoaded(config: MonoConfig)' and 'onRuntimeReady(api: RuntimeAPI)' - * blazor calls 'beforeStart' and 'afterStarted' - */ - libraryInitializers?: any[]; /** * definition of assets to load along with the runtime. */ @@ -201,7 +183,10 @@ interface ResourceGroups { readonly satelliteResources?: { [cultureName: string]: ResourceList; }; - readonly libraryInitializers?: ResourceList; + readonly libraryInitializers?: { + readonly onRuntimeConfigLoaded: ResourceList; + readonly onRuntimeReady: ResourceList; + }; readonly extensions?: ResourceExtensions; readonly vfs?: { [virtualPath: string]: ResourceList; @@ -259,12 +244,79 @@ interface AssetEntry extends ResourceRequest { */ pendingDownload?: LoadingResource; } -type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads" | "js-module-runtime" | "js-module-dotnet" | "js-module-native" | "symbols"; +type AssetBehaviours = + /** + * Load asset as a managed resource assembly. + */ + "resource" + /** + * Load asset as a managed assembly. + */ + | "assembly" + /** + * Load asset as a managed debugging information. + */ + | "pdb" + /** + * Store asset into the native heap. + */ + | "heap" + /** + * Load asset as an ICU data archive. + */ + | "icu" + /** + * Load asset into the virtual filesystem (for fopen, File.Open, etc). + */ + | "vfs" + /** + * The binary of the dotnet runtime. + */ + | "dotnetwasm" + /** + * The javascript module for threads. + */ + | "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 that came from nuget package . + */ + | "js-module-library-initializer" + /** + * The javascript module for threads. + */ + | "symbols"; declare const enum GlobalizationMode { + /** + * Load sharded ICU data. + */ Sharded = "sharded", + /** + * Load all ICU data. + */ All = "all", + /** + * Operate in invariant globalization mode. + */ Invariant = "invariant", + /** + * Use user defined icu file. + */ Custom = "custom", + /** + * Operate in hybrid globalization mode with small ICU files, using native platform functions. + */ Hybrid = "hybrid" } type DotnetModuleConfig = { @@ -286,6 +338,7 @@ type APIType = { getAssemblyExports(assemblyName: string): Promise; setModuleImports(moduleName: string, moduleImports: any): void; getConfig: () => MonoConfig; + invokeLibraryInitializers: (functionName: string, args: any[]) => Promise; setHeapB32: (offset: NativePointer, value: number | boolean) => void; setHeapU8: (offset: NativePointer, value: number) => void; setHeapU16: (offset: NativePointer, value: number) => void; @@ -379,4 +432,4 @@ declare global { } declare const createDotnetRuntime: CreateDotnetRuntimeType; -export { AssetEntry, CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit }; +export { AssetEntry, CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, GlobalizationMode, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit }; diff --git a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts index 5897faedb83e..895f52ff6724 100644 --- a/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts +++ b/src/Components/Web.JS/src/Boot.WebAssembly.Common.ts @@ -8,7 +8,7 @@ import * as Environment from './Environment'; import { BINDING, monoPlatform, dispatcher } from './Platform/Mono/MonoPlatform'; import { renderBatch, getRendererer, attachRootComponentToElement, attachRootComponentToLogicalElement } from './Rendering/Renderer'; import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch'; -import { Pointer } from './Platform/Platform'; +import { PlatformApi, Pointer } from './Platform/Platform'; import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions'; import { addDispatchEventMiddleware } from './Rendering/WebRendererInteropMethods'; import { JSInitializer } from './JSInitializers/JSInitializers'; @@ -106,10 +106,9 @@ export async function startWebAssembly(options?: Partial { - if (typeof Module !== 'undefined' && Module.printErr) { + if (typeof Module !== 'undefined' && Module.err) { // Logs it, and causes the error UI to appear - Module.printErr(error); + Module.err(error); } else { // The error must have happened so early we didn't yet set up the error UI, so just log to console console.error(error); diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts deleted file mode 100644 index 039f24e66071..000000000000 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { MonoConfig } from 'dotnet'; -import { WebAssemblyStartOptions } from '../Platform/WebAssemblyStartOptions'; -import { BlazorInitializer, JSInitializer } from './JSInitializers'; - -export async function invokeOnBeforeStart(moduleConfig: MonoConfig, options: Partial): Promise { - const initializerArguments = [options, moduleConfig.resources?.extensions ?? {}]; - const jsInitializer = new JSInitializer(); - - const beforeStartPromises: Promise[] = []; - if (moduleConfig.libraryInitializers) { - for (let i = 0; i < moduleConfig.libraryInitializers.length; i++) { - const initializer = moduleConfig.libraryInitializers[i]; - const blazorInitializer = initializer as Partial; - if (blazorInitializer === undefined) { - continue; - } - - const { beforeStart: beforeStart, afterStarted: afterStarted } = blazorInitializer; - if (afterStarted) { - jsInitializer.addAfterStartedCallback(afterStarted); - } - - if (beforeStart) { - beforeStartPromises.push(beforeStart(...initializerArguments)); - } - } - } - - await Promise.all(beforeStartPromises); - return jsInitializer; -} diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts index f6fd9161b2dd..7cd81e392b4d 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts @@ -11,10 +11,6 @@ export type BlazorInitializer = { beforeStart: BeforeBlazorStartedCallback, afte export class JSInitializer { private afterStartedCallbacks: AfterBlazorStartedCallback[] = []; - addAfterStartedCallback(afterStartedCallback: AfterBlazorStartedCallback): void { - this.afterStartedCallbacks.push(afterStartedCallback); - } - async importInitializersAsync(initializerFiles: string[], initializerArguments: unknown[]): Promise { // This code is not called on WASM, because library intializers are imported by runtime. diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 88ba6800eba7..9b68e70f7243 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -12,7 +12,6 @@ import { WebAssemblyBootResourceType, WebAssemblyStartOptions } from '../WebAsse import { Blazor } from '../../GlobalExports'; import { DotnetModuleConfig, EmscriptenModule, MonoConfig, ModuleAPI, RuntimeAPI, GlobalizationMode } from 'dotnet'; import { BINDINGType, MONOType } from 'dotnet/dotnet-legacy'; -import { invokeOnBeforeStart } from '../../JSInitializers/JSInitializers.WebAssembly'; // initially undefined and only fully initialized after createEmscriptenModuleInstance() export let BINDING: BINDINGType = undefined as any; @@ -171,14 +170,14 @@ async function importDotnetJs(startOptions: Partial): P return await import(/* webpackIgnore: true */ absoluteSrc); } -function prepareRuntimeConfig(options: Partial, platformApi: any): DotnetModuleConfig { +function prepareRuntimeConfig(options: Partial): DotnetModuleConfig { const config: MonoConfig = { maxParallelDownloads: 1000000, // disable throttling parallel downloads enableDownloadRetry: false, // disable retry downloads applicationEnvironment: options.environment, }; - const onConfigLoaded = async (loadedConfig: MonoConfig): Promise => { + const onConfigLoaded = async (loadedConfig: MonoConfig, { INTERNAL: MONO_INTERNAL }) => { if (!loadedConfig.environmentVariables) { loadedConfig.environmentVariables = {}; } @@ -189,34 +188,34 @@ function prepareRuntimeConfig(options: Partial, platfor Blazor._internal.getApplicationEnvironment = () => loadedConfig.applicationEnvironment!; - platformApi.jsInitializer = await invokeOnBeforeStart(loadedConfig, options); + const initializerArguments = [options, loadedConfig.resources?.extensions ?? {}]; + await MONO_INTERNAL.invokeLibraryInitializers('beforeStart', initializerArguments); }; const moduleConfig = (window['Module'] || {}) as typeof Module; // TODO (moduleConfig as any).preloadPlugins = []; // why do we need this ? const dotnetModuleConfig: DotnetModuleConfig = { ...moduleConfig, - onConfigLoaded, + onConfigLoaded: (onConfigLoaded as (config: MonoConfig) => void | Promise), onDownloadResourceProgress: setProgress, config, disableDotnet6Compatibility: false, - print, - printErr, + out: print, + err: printErr, }; return dotnetModuleConfig; } async function createRuntimeInstance(options: Partial): Promise { - const platformApi: Partial = {}; const { dotnet } = await importDotnetJs(options); - const moduleConfig = prepareRuntimeConfig(options, platformApi); + const moduleConfig = prepareRuntimeConfig(options); const anyDotnet = (dotnet as any); anyDotnet.withStartupOptions(options).withModuleConfig(moduleConfig); runtime = await dotnet.create(); - const { MONO: mono, BINDING: binding, Module: module, setModuleImports, INTERNAL: mono_internal, getConfig } = runtime; + const { MONO: mono, BINDING: binding, Module: module, setModuleImports, INTERNAL: mono_internal, getConfig, invokeLibraryInitializers } = runtime; Module = module; BINDING = binding; MONO = mono; @@ -236,7 +235,9 @@ async function createRuntimeInstance(options: Partial): }); attachInteropInvoker(); - return platformApi as PlatformApi; + return { + invokeLibraryInitializers, + }; } function setProgress(resourcesLoaded, totalResources) { diff --git a/src/Components/Web.JS/src/Platform/Platform.ts b/src/Components/Web.JS/src/Platform/Platform.ts index 20a46499b6a2..dbe5523c1796 100644 --- a/src/Components/Web.JS/src/Platform/Platform.ts +++ b/src/Components/Web.JS/src/Platform/Platform.ts @@ -3,7 +3,6 @@ import { MonoObject, MonoString, MonoArray } from 'dotnet/dotnet-legacy'; import { WebAssemblyStartOptions } from './WebAssemblyStartOptions'; -import { JSInitializer } from '../JSInitializers/JSInitializers'; export interface Platform { start(options: Partial): Promise; @@ -29,7 +28,7 @@ export interface Platform { } export type PlatformApi = { - jsInitializer: JSInitializer + invokeLibraryInitializers(functionName: string, args: unknown[]): Promise; } export interface HeapLock { From af74e799bb7df8cb0ad13c3cf9e05e2ab406a5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 12 Jul 2023 14:33:00 +0200 Subject: [PATCH 14/14] Fix --- src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 9b68e70f7243..5178842558a0 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -177,7 +177,7 @@ function prepareRuntimeConfig(options: Partial): Dotnet applicationEnvironment: options.environment, }; - const onConfigLoaded = async (loadedConfig: MonoConfig, { INTERNAL: MONO_INTERNAL }) => { + const onConfigLoaded = async (loadedConfig: MonoConfig, { invokeLibraryInitializers }) => { if (!loadedConfig.environmentVariables) { loadedConfig.environmentVariables = {}; } @@ -189,7 +189,7 @@ function prepareRuntimeConfig(options: Partial): Dotnet Blazor._internal.getApplicationEnvironment = () => loadedConfig.applicationEnvironment!; const initializerArguments = [options, loadedConfig.resources?.extensions ?? {}]; - await MONO_INTERNAL.invokeLibraryInitializers('beforeStart', initializerArguments); + await invokeLibraryInitializers('beforeStart', initializerArguments); }; const moduleConfig = (window['Module'] || {}) as typeof Module;