diff --git a/src/Components/Shared/src/BrowserNavigationManagerInterop.cs b/src/Components/Shared/src/BrowserNavigationManagerInterop.cs index bb550b21fc7d..d1ee8fc175ee 100644 --- a/src/Components/Shared/src/BrowserNavigationManagerInterop.cs +++ b/src/Components/Shared/src/BrowserNavigationManagerInterop.cs @@ -10,9 +10,9 @@ internal static class BrowserNavigationManagerInterop public const string EnableNavigationInterception = Prefix + "enableNavigationInterception"; - public const string GetLocationHref = Prefix + "getUnmarshalledLocationHref"; + public const string GetLocationHref = Prefix + "getLocationHref"; - public const string GetBaseUri = Prefix + "getUnmarshalledBaseURI"; + public const string GetBaseUri = Prefix + "getBaseURI"; public const string NavigateTo = Prefix + "navigateTo"; diff --git a/src/Components/Web.JS/src/Boot.WebAssembly.ts b/src/Components/Web.JS/src/Boot.WebAssembly.ts index a8c84ee31957..b58da89634d5 100644 --- a/src/Components/Web.JS/src/Boot.WebAssembly.ts +++ b/src/Components/Web.JS/src/Boot.WebAssembly.ts @@ -5,14 +5,14 @@ import { DotNet } from '@microsoft/dotnet-js-interop'; import { Blazor } from './GlobalExports'; import * as Environment from './Environment'; -import { byteArrayBeingTransferred, Module, BINDING, monoPlatform } from './Platform/Mono/MonoPlatform'; +import { Module, BINDING, monoPlatform } from './Platform/Mono/MonoPlatform'; import { renderBatch, getRendererer, attachRootComponentToElement, attachRootComponentToLogicalElement } from './Rendering/Renderer'; import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch'; import { shouldAutoStart } from './BootCommon'; import { WebAssemblyResourceLoader } from './Platform/WebAssemblyResourceLoader'; import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader'; import { BootConfigResult } from './Platform/BootConfig'; -import { Pointer, System_Array, System_Boolean, System_Byte, System_Int, System_Object, System_String } from './Platform/Platform'; +import { Pointer } from './Platform/Platform'; import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions'; import { WebAssemblyComponentAttacher } from './Platform/WebAssemblyComponentAttacher'; import { discoverComponents, discoverPersistedState, WebAssemblyComponentDescriptor } from './Services/ComponentDescriptorDiscovery'; @@ -52,9 +52,9 @@ async function boot(options?: Partial): Promise { // Configure JS interop Blazor._internal.invokeJSFromDotNet = invokeJSFromDotNet; + Blazor._internal.invokeJSJson = invokeJSJson; Blazor._internal.endInvokeDotNetFromJS = endInvokeDotNetFromJS; Blazor._internal.receiveByteArray = receiveByteArray; - Blazor._internal.retrieveByteArray = retrieveByteArray; // Configure environment for execution under Mono WebAssembly with shared-memory rendering const platform = Environment.setPlatform(monoPlatform); @@ -73,12 +73,6 @@ async function boot(options?: Partial): Promise { } }; - // Configure navigation via JS Interop - const getBaseUri = Blazor._internal.navigationManager.getBaseURI; - const getLocationHref = Blazor._internal.navigationManager.getLocationHref; - Blazor._internal.navigationManager.getUnmarshalledBaseURI = () => BINDING.js_string_to_mono_string(getBaseUri()); - Blazor._internal.navigationManager.getUnmarshalledLocationHref = () => BINDING.js_string_to_mono_string(getLocationHref()); - Blazor._internal.navigationManager.listenForNavigationEvents(async (uri: string, state: string | undefined, intercepted: boolean): Promise => { await DotNet.invokeMethodAsync( 'Microsoft.AspNetCore.Components.WebAssembly', @@ -114,13 +108,13 @@ async function boot(options?: Partial): Promise { Blazor._internal.registeredComponents = { getRegisteredComponentsCount: () => componentAttacher.getCount(), getId: (index) => componentAttacher.getId(index), - getAssembly: (id) => BINDING.js_string_to_mono_string(componentAttacher.getAssembly(id)), - getTypeName: (id) => BINDING.js_string_to_mono_string(componentAttacher.getTypeName(id)), - getParameterDefinitions: (id) => BINDING.js_string_to_mono_string(componentAttacher.getParameterDefinitions(id) || ''), - getParameterValues: (id) => BINDING.js_string_to_mono_string(componentAttacher.getParameterValues(id) || ''), + getAssembly: (id) => componentAttacher.getAssembly(id), + getTypeName: (id) => componentAttacher.getTypeName(id), + getParameterDefinitions: (id) => componentAttacher.getParameterDefinitions(id) || '', + getParameterValues: (id) => componentAttacher.getParameterValues(id) || '', }; - Blazor._internal.getPersistedState = () => BINDING.js_string_to_mono_string(discoverPersistedState(document) || ''); + Blazor._internal.getPersistedState = () => discoverPersistedState(document) || ''; Blazor._internal.attachRootComponentToElement = (selector, componentId, rendererId: any) => { const element = componentAttacher.resolveRegisteredElement(selector); @@ -191,26 +185,21 @@ function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any): } } -function endInvokeDotNetFromJS(callId: System_String, success: System_Boolean, resultJsonOrErrorMessage: System_String): void { - const callIdString = BINDING.conv_string(callId)!; - const successBool = (success as any as number) !== 0; - const resultJsonOrErrorMessageString = BINDING.conv_string(resultJsonOrErrorMessage)!; - DotNet.jsCallDispatcher.endInvokeDotNetFromJS(callIdString, successBool, resultJsonOrErrorMessageString); +function invokeJSJson(identifier: string, targetInstanceId: number, resultType: number, argsJson: string, asyncHandle: number): string | null { + if (asyncHandle !== 0) { + DotNet.jsCallDispatcher.beginInvokeJSFromDotNet(asyncHandle, identifier, argsJson, resultType, targetInstanceId); + return null; + } else { + return DotNet.jsCallDispatcher.invokeJSFromDotNet(identifier, argsJson, resultType, targetInstanceId); + } } -function receiveByteArray(id: System_Int, data: System_Array): void { - const idLong = id as unknown as number; - const dataByteArray = monoPlatform.toUint8Array(data); - DotNet.jsCallDispatcher.receiveByteArray(idLong, dataByteArray); +function endInvokeDotNetFromJS(callId: string, success: boolean, resultJsonOrErrorMessage: string): void { + DotNet.jsCallDispatcher.endInvokeDotNetFromJS(callId, success, resultJsonOrErrorMessage); } -function retrieveByteArray(): System_Object { - if (byteArrayBeingTransferred === null) { - throw new Error('Byte array not available for transfer'); - } - - const typedArray = BINDING.js_typed_array_to_array(byteArrayBeingTransferred); - return typedArray; +function receiveByteArray(id: number, data: Uint8Array): void { + DotNet.jsCallDispatcher.receiveByteArray(id, data); } function inAuthRedirectIframe(): boolean { diff --git a/src/Components/Web.JS/src/GlobalExports.ts b/src/Components/Web.JS/src/GlobalExports.ts index 2e2075ed321f..21f7c24a34f0 100644 --- a/src/Components/Web.JS/src/GlobalExports.ts +++ b/src/Components/Web.JS/src/GlobalExports.ts @@ -12,7 +12,7 @@ import { NavigationLock } from './NavigationLock'; import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler'; import { CircuitStartOptions } from './Platform/Circuits/CircuitStartOptions'; import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions'; -import { Platform, Pointer, System_String, System_Array, System_Object, System_Boolean, System_Byte, System_Int } from './Platform/Platform'; +import { Platform, Pointer } from './Platform/Platform'; import { getNextChunk, receiveDotNetDataStream } from './StreamingInterop'; import { RootComponentsFunctions } from './Rendering/JSRootComponents'; import { attachWebRendererInterop } from './Rendering/WebRendererInteropMethods'; @@ -29,41 +29,49 @@ interface IBlazor { rootComponents: typeof RootComponentsFunctions; _internal: { - navigationManager: typeof navigationManagerInternalFunctions | any, - domWrapper: typeof domFunctions, - Virtualize: typeof Virtualize, - PageTitle: typeof PageTitle, + navigationManager: typeof navigationManagerInternalFunctions | any; + domWrapper: typeof domFunctions; + Virtualize: typeof Virtualize; + PageTitle: typeof PageTitle; forceCloseConnection?: () => Promise; - InputFile?: typeof InputFile, - NavigationLock: typeof NavigationLock, + InputFile?: typeof InputFile; + NavigationLock: typeof NavigationLock; invokeJSFromDotNet?: (callInfo: Pointer, arg0: any, arg1: any, arg2: any) => any; - endInvokeDotNetFromJS?: (callId: System_String, success: System_Boolean, resultJsonOrErrorMessage: System_String) => void; - receiveByteArray?: (id: System_Int, data: System_Array) => void; - retrieveByteArray?: () => System_Object; - getPersistedState?: () => System_String; + invokeJSJson?: (identifier: string, targetInstanceId: number, resultType: number, argsJson: string, asyncHandle: number) => string | null; + endInvokeDotNetFromJS?: (callId: string, success: boolean, resultJsonOrErrorMessage: string) => void; + receiveByteArray?: (id: number, data: Uint8Array) => void; + getPersistedState?: () => string; attachRootComponentToElement?: (arg0: any, arg1: any, arg2: any, arg3: any) => void; registeredComponents?: { - getRegisteredComponentsCount: () => number, - getId: (index) => number, - getAssembly: (id) => System_String, - getTypeName: (id) => System_String, - getParameterDefinitions: (id) => System_String, - getParameterValues: (id) => any, + getRegisteredComponentsCount: () => number; + getId: (index) => number; + getAssembly: (id) => string; + getTypeName: (id) => string; + getParameterDefinitions: (id) => string; + getParameterValues: (id) => any; }; - renderBatch?: (browserRendererId: number, batchAddress: Pointer) => void, - getConfig?: (dotNetFileName: System_String) => System_Object | undefined, - getApplicationEnvironment?: () => System_String, - dotNetCriticalError?: any - loadLazyAssembly?: any, - loadSatelliteAssemblies?: any, - sendJSDataStream?: (data: any, streamId: number, chunkSize: number) => void, - getJSDataStreamChunk?: (data: any, position: number, chunkSize: number) => Promise, - receiveDotNetDataStream?: (streamId: number, data: any, bytesRead: number, errorMessage: string) => void, - attachWebRendererInterop?: typeof attachWebRendererInterop, + renderBatch?: (browserRendererId: number, batchAddress: Pointer) => void; + getConfig?: (fileName: string) => Uint8Array | undefined; + getApplicationEnvironment?: () => string; + dotNetCriticalError?: any; + loadLazyAssembly?: any; + loadSatelliteAssemblies?: any; + sendJSDataStream?: (data: any, streamId: number, chunkSize: number) => void; + getJSDataStreamChunk?: (data: any, position: number, chunkSize: number) => Promise; + receiveDotNetDataStream?: (streamId: number, data: any, bytesRead: number, errorMessage: string) => void; + attachWebRendererInterop?: typeof attachWebRendererInterop; + + // JSExport APIs + dotNetExports?: { + InvokeDotNet: (assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number, argsJson: string) => string | null; + EndInvokeJS: (argsJson: string) => void; + BeginInvokeDotNet: (callId: string | null, assemblyNameOrDotNetObjectId: string, methodIdentifier: string, argsJson: string) => void; + ReceiveByteArrayFromJS: (id: number, data: Uint8Array) => void; + } // APIs invoked by hot reload - applyHotReload?: (id: string, metadataDelta: string, ilDelta: string, pdbDelta: string | undefined) => void, - getApplyUpdateCapabilities?: () => string, + applyHotReload?: (id: string, metadataDelta: string, ilDelta: string, pdbDelta: string | undefined) => void; + getApplyUpdateCapabilities?: () => string; } } diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 54acedf007d9..8a7bc5f96df1 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -369,6 +369,12 @@ async function createRuntimeInstance(resourceLoader: WebAssemblyResourceLoader): setModuleImports('blazor-internal', { Blazor: { _internal: Blazor._internal }, }); + const exports = await runtime.getAssemblyExports('Microsoft.AspNetCore.Components.WebAssembly'); + Object.assign(Blazor._internal, { + dotNetExports: { + ...exports.Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime, + }, + }); attachInteropInvoker(); if (resourceLoader.bootConfig.debugBuild && resourceLoader.bootConfig.cacheBootResources) { resourceLoader.logToConsole(); @@ -412,7 +418,6 @@ async function loadSatelliteAssemblies(resourceLoader: WebAssemblyResourceLoader })); } - async function loadLazyAssembly(resourceLoader: WebAssemblyResourceLoader, assemblyNameToLoad: string): Promise<{ dll: Uint8Array, pdb: Uint8Array | null }> { const resources = resourceLoader.bootConfig.resources; const lazyAssemblies = resources.lazyAssembly; @@ -449,19 +454,7 @@ function getArrayDataPointer(array: System_Array): number { return array + 12; // First byte from here is length, then following bytes are entries } -function bindStaticMethod(assembly: string, typeName: string, method: string) { - // Fully qualified name looks like this: "[debugger-test] Math:IntAdd" - const fqn = `[${assembly}] ${typeName}:${method}`; - return BINDING.bind_static_method(fqn); -} - -export let byteArrayBeingTransferred: Uint8Array | null = null; function attachInteropInvoker(): void { - const dotNetDispatcherInvokeMethodHandle = bindStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime', 'InvokeDotNet'); - const dotNetDispatcherBeginInvokeMethodHandle = bindStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime', 'BeginInvokeDotNet'); - const dotNetDispatcherEndInvokeJSMethodHandle = bindStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime', 'EndInvokeJS'); - const dotNetDispatcherNotifyByteArrayAvailableMethodHandle = bindStaticMethod('Microsoft.AspNetCore.Components.WebAssembly', 'Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime', 'NotifyByteArrayAvailable'); - DotNet.attachDispatcher({ beginInvokeDotNetFromJS: (callId: number, assemblyName: string | null, methodIdentifier: string, dotNetObjectId: any | null, argsJson: string): void => { assertHeapIsNotLocked(); @@ -474,7 +467,7 @@ function attachInteropInvoker(): void { ? dotNetObjectId.toString() : assemblyName; - dotNetDispatcherBeginInvokeMethodHandle( + Blazor._internal.dotNetExports!.BeginInvokeDotNet!( callId ? callId.toString() : null, assemblyNameOrDotNetObjectId, methodIdentifier, @@ -482,18 +475,17 @@ function attachInteropInvoker(): void { ); }, endInvokeJSFromDotNet: (asyncHandle, succeeded, serializedArgs): void => { - dotNetDispatcherEndInvokeJSMethodHandle(serializedArgs); + Blazor._internal.dotNetExports!.EndInvokeJS(serializedArgs); }, sendByteArray: (id: number, data: Uint8Array): void => { - byteArrayBeingTransferred = data; - dotNetDispatcherNotifyByteArrayAvailableMethodHandle(id); + Blazor._internal.dotNetExports!.ReceiveByteArrayFromJS(id, data); }, invokeDotNetFromJS: (assemblyName, methodIdentifier, dotNetObjectId, argsJson) => { assertHeapIsNotLocked(); - return dotNetDispatcherInvokeMethodHandle( + return Blazor._internal.dotNetExports!.InvokeDotNet( assemblyName ? assemblyName : null, methodIdentifier, - dotNetObjectId ? dotNetObjectId.toString() : null, + dotNetObjectId ?? 0, argsJson, ) as string; }, diff --git a/src/Components/Web.JS/src/Platform/WebAssemblyConfigLoader.ts b/src/Components/Web.JS/src/Platform/WebAssemblyConfigLoader.ts index 19011f2d07ae..1fd2cc50ce33 100644 --- a/src/Components/Web.JS/src/Platform/WebAssemblyConfigLoader.ts +++ b/src/Components/Web.JS/src/Platform/WebAssemblyConfigLoader.ts @@ -3,22 +3,19 @@ import { BootConfigResult } from './BootConfig'; import { WebAssemblyStartOptions } from './WebAssemblyStartOptions'; -import { System_String, System_Object } from './Platform'; import { Blazor } from '../GlobalExports'; -import { BINDING } from './Mono/MonoPlatform'; export class WebAssemblyConfigLoader { static async initAsync(bootConfigResult: BootConfigResult, startOptions: Partial): Promise { - Blazor._internal.getApplicationEnvironment = () => BINDING.js_string_to_mono_string(bootConfigResult.applicationEnvironment); + Blazor._internal.getApplicationEnvironment = () => bootConfigResult.applicationEnvironment; const configFiles = await Promise.all((bootConfigResult.bootConfig.config || []) .filter(name => name === 'appsettings.json' || name === `appsettings.${bootConfigResult.applicationEnvironment}.json`) .map(async name => ({ name, content: await getConfigBytes(name) }))); - Blazor._internal.getConfig = (dotNetFileName: System_String) : System_Object | undefined => { - const fileName = BINDING.conv_string(dotNetFileName); + Blazor._internal.getConfig = (fileName: string) : Uint8Array | undefined => { const resolvedFile = configFiles.find(f => f.name === fileName); - return resolvedFile ? BINDING.js_typed_array_to_array(resolvedFile.content) : undefined; + return resolvedFile ? resolvedFile.content : undefined; }; async function getConfigBytes(file: string): Promise { diff --git a/src/Components/Web/src/Forms/InputFile.cs b/src/Components/Web/src/Forms/InputFile.cs index f1ed4c12c4af..6f3000faf73f 100644 --- a/src/Components/Web/src/Forms/InputFile.cs +++ b/src/Components/Web/src/Forms/InputFile.cs @@ -14,8 +14,6 @@ public class InputFile : ComponentBase, IInputFileJsCallbacks, IDisposable { private ElementReference _inputFileElement; - private IJSUnmarshalledRuntime? _jsUnmarshalledRuntime; - private InputFileJsCallbacksRelay? _jsCallbacksRelay; [Inject] @@ -46,12 +44,6 @@ public ElementReference? Element protected set => _inputFileElement = value!.Value; } - /// - protected override void OnInitialized() - { - _jsUnmarshalledRuntime = JSRuntime as IJSUnmarshalledRuntime; - } - /// protected override async Task OnAfterRenderAsync(bool firstRender) { diff --git a/src/Components/Web/src/PublicAPI.Unshipped.txt b/src/Components/Web/src/PublicAPI.Unshipped.txt index 8c8ed39e008f..fe9ed7ca8b89 100644 --- a/src/Components/Web/src/PublicAPI.Unshipped.txt +++ b/src/Components/Web/src/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +*REMOVED*override Microsoft.AspNetCore.Components.Forms.InputFile.OnInitialized() -> void Microsoft.AspNetCore.Components.Web.HtmlComponent Microsoft.AspNetCore.Components.Web.HtmlComponent.ToHtmlString() -> string! Microsoft.AspNetCore.Components.Web.HtmlComponent.WaitForQuiescenceAsync() -> System.Threading.Tasks.Task! diff --git a/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs b/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs index a798b709a9e6..0b35b9509204 100644 --- a/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs +++ b/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs @@ -3,19 +3,36 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices.JavaScript; namespace WebAssembly.JSInterop; -/// -/// Methods that map to the functions compiled into the Mono WebAssembly runtime, -/// as defined by 'mono_add_internal_call' calls in driver.c. -/// -internal static class InternalCalls +internal static partial class InternalCalls { - // The exact namespace, type, and method names must match the corresponding entries - // in driver.c in the Mono distribution - /// See: https://github.com/mono/mono/blob/90574987940959fe386008a850982ea18236a533/sdks/wasm/src/driver.c#L318-L319 - + // This method only exists for backwards compatibility and will be removed in the future. + // The exact namespace, type, and method name must match the corresponding entries + // in driver.c in the Mono distribution. + // See: https://github.com/mono/mono/blob/90574987940959fe386008a850982ea18236a533/sdks/wasm/src/driver.c#L318-L319 [MethodImpl(MethodImplOptions.InternalCall)] + [Obsolete] public static extern TRes InvokeJS(out string exception, ref JSCallInfo callInfo, [AllowNull] T0 arg0, [AllowNull] T1 arg1, [AllowNull] T2 arg2); + + [JSImport("Blazor._internal.invokeJSJson", "blazor-internal")] + public static partial string InvokeJSJson( + string identifier, + [JSMarshalAs] long targetInstanceId, + int resultType, + string argsJson, + [JSMarshalAs] long asyncHandle); + + [JSImport("Blazor._internal.endInvokeDotNetFromJS", "blazor-internal")] + public static partial void EndInvokeDotNetFromJS( + string? id, + bool success, + string jsonOrError); + + [JSImport("Blazor._internal.receiveByteArray", "blazor-internal")] + public static partial void ReceiveByteArray( + int id, + byte[] data); } diff --git a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj index 5699e0efbb25..9d8b54aa41e3 100644 --- a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj +++ b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj @@ -8,6 +8,7 @@ true enable true + true false diff --git a/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs b/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs index 5a72fe8275d0..6aeccbcc43ec 100644 --- a/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs +++ b/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs @@ -25,35 +25,20 @@ protected WebAssemblyJSRuntime() /// protected override string InvokeJS(string identifier, [StringSyntax(StringSyntaxAttribute.Json)] string? argsJson, JSCallResultType resultType, long targetInstanceId) { - var callInfo = new JSCallInfo + try { - FunctionIdentifier = identifier, - TargetInstanceId = targetInstanceId, - ResultType = resultType, - MarshalledCallArgsJson = argsJson ?? "[]", - MarshalledCallAsyncHandle = default - }; - - var result = InternalCalls.InvokeJS(out var exception, ref callInfo, null, null, null); - - return exception != null - ? throw new JSException(exception) - : result; + return InternalCalls.InvokeJSJson(identifier, targetInstanceId, (int)resultType, argsJson ?? "[]", 0); + } + catch (Exception ex) + { + throw new JSException(ex.Message, ex); + } } /// protected override void BeginInvokeJS(long asyncHandle, string identifier, [StringSyntax(StringSyntaxAttribute.Json)] string? argsJson, JSCallResultType resultType, long targetInstanceId) { - var callInfo = new JSCallInfo - { - FunctionIdentifier = identifier, - TargetInstanceId = targetInstanceId, - ResultType = resultType, - MarshalledCallArgsJson = argsJson ?? "[]", - MarshalledCallAsyncHandle = asyncHandle - }; - - InternalCalls.InvokeJS(out _, ref callInfo, null, null, null); + InternalCalls.InvokeJSJson(identifier, targetInstanceId, (int)resultType, argsJson ?? "[]", asyncHandle); } /// @@ -63,18 +48,13 @@ protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNet var resultJsonOrErrorMessage = dispatchResult.Success ? dispatchResult.ResultJson! : dispatchResult.Exception!.ToString(); -#pragma warning disable CS0618 // Type or member is obsolete - InvokeUnmarshalled("Blazor._internal.endInvokeDotNetFromJS", - callInfo.CallId, dispatchResult.Success, resultJsonOrErrorMessage); -#pragma warning restore CS0618 // Type or member is obsolete + InternalCalls.EndInvokeDotNetFromJS(callInfo.CallId, dispatchResult.Success, resultJsonOrErrorMessage); } /// protected override void SendByteArray(int id, byte[] data) { -#pragma warning disable CS0618 // Type or member is obsolete - InvokeUnmarshalled("Blazor._internal.receiveByteArray", id, data); -#pragma warning restore CS0618 // Type or member is obsolete + InternalCalls.ReceiveByteArray(id, data); } [Obsolete("This method is obsolete. Use JSImportAttribute instead.")] diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj b/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj index 576eb988427f..3fe158db76c6 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj +++ b/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/test/WebAssemblyAuthenticationServiceCollectionExtensionsTests.cs b/src/Components/WebAssembly/WebAssembly.Authentication/test/WebAssemblyAuthenticationServiceCollectionExtensionsTests.cs index 8b403aa885a4..e20acfac4fab 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/test/WebAssemblyAuthenticationServiceCollectionExtensionsTests.cs +++ b/src/Components/WebAssembly/WebAssembly.Authentication/test/WebAssemblyAuthenticationServiceCollectionExtensionsTests.cs @@ -17,7 +17,7 @@ public class WebAssemblyAuthenticationServiceCollectionExtensionsTests [Fact] public void CanResolve_AccessTokenProvider() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddApiAuthorization(); var host = builder.Build(); @@ -27,7 +27,7 @@ public void CanResolve_AccessTokenProvider() [Fact] public void CanResolve_IRemoteAuthenticationService() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddApiAuthorization(); var host = builder.Build(); @@ -37,7 +37,7 @@ public void CanResolve_IRemoteAuthenticationService() [Fact] public void ApiAuthorizationOptions_ConfigurationDefaultsGetApplied() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddApiAuthorization(); var host = builder.Build(); @@ -71,7 +71,7 @@ public void ApiAuthorizationOptions_ConfigurationDefaultsGetApplied() [Fact] public void ApiAuthorizationOptionsConfigurationCallback_GetsCalledOnce() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var calls = 0; builder.Services.AddApiAuthorization(options => { @@ -98,7 +98,7 @@ public void ApiAuthorizationOptionsConfigurationCallback_GetsCalledOnce() [Fact] public void ApiAuthorizationTestAuthenticationState_SetsUpConfiguration() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var calls = 0; builder.Services.AddApiAuthorization(options => calls++); @@ -124,7 +124,7 @@ public void ApiAuthorizationTestAuthenticationState_SetsUpConfiguration() [Fact] public void ApiAuthorizationTestAuthenticationState_NoCallback_SetsUpConfiguration() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddApiAuthorization(); var host = builder.Build(); @@ -147,7 +147,7 @@ public void ApiAuthorizationTestAuthenticationState_NoCallback_SetsUpConfigurati [Fact] public void ApiAuthorizationCustomAuthenticationStateAndAccount_SetsUpConfiguration() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var calls = 0; builder.Services.AddApiAuthorization(options => calls++); @@ -173,7 +173,7 @@ public void ApiAuthorizationCustomAuthenticationStateAndAccount_SetsUpConfigurat [Fact] public void ApiAuthorizationTestAuthenticationStateAndAccount_NoCallback_SetsUpConfiguration() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddApiAuthorization(); var host = builder.Build(); @@ -196,7 +196,7 @@ public void ApiAuthorizationTestAuthenticationStateAndAccount_NoCallback_SetsUpC [Fact] public void ApiAuthorizationOptions_DefaultsCanBeOverriden() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddApiAuthorization(options => { options.AuthenticationPaths.LogInPath = "a"; @@ -247,7 +247,7 @@ public void ApiAuthorizationOptions_DefaultsCanBeOverriden() [Fact] public void OidcOptions_ConfigurationDefaultsGetApplied() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.Replace(ServiceDescriptor.Singleton()); builder.Services.AddOidcAuthentication(options => { }); var host = builder.Build(); @@ -286,7 +286,7 @@ public void OidcOptions_ConfigurationDefaultsGetApplied() [Fact] public void OidcOptions_DefaultsCanBeOverriden() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddOidcAuthentication(options => { options.AuthenticationPaths.LogInPath = "a"; @@ -348,7 +348,7 @@ public void OidcOptions_DefaultsCanBeOverriden() [Fact] public void AddOidc_ConfigurationGetsCalledOnce() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var calls = 0; builder.Services.AddOidcAuthentication(options => calls++); @@ -365,7 +365,7 @@ public void AddOidc_ConfigurationGetsCalledOnce() [Fact] public void AddOidc_CustomState_SetsUpConfiguration() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var calls = 0; builder.Services.AddOidcAuthentication(options => options.ProviderOptions.Authority = (++calls).ToString(CultureInfo.InvariantCulture)); @@ -387,7 +387,7 @@ public void AddOidc_CustomState_SetsUpConfiguration() [Fact] public void AddOidc_CustomStateAndAccount_SetsUpConfiguration() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var calls = 0; builder.Services.AddOidcAuthentication(options => options.ProviderOptions.Authority = (++calls).ToString(CultureInfo.InvariantCulture)); @@ -409,7 +409,7 @@ public void AddOidc_CustomStateAndAccount_SetsUpConfiguration() [Fact] public void OidcProviderOptionsAndDependencies_NotResolvedFromRootScope() { - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var calls = 0; diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs index cdbdb9be7e3e..dcf08f581bf5 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs @@ -6,8 +6,6 @@ using System.Runtime.InteropServices.JavaScript; using System.Runtime.Loader; using System.Runtime.Versioning; -using Microsoft.AspNetCore.Components.WebAssembly.Services; -using Microsoft.JSInterop; namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting; @@ -19,12 +17,9 @@ internal partial class WebAssemblyCultureProvider internal const string GetSatelliteAssemblies = "window.Blazor._internal.getSatelliteAssemblies"; internal const string ReadSatelliteAssemblies = "window.Blazor._internal.readSatelliteAssemblies"; - private readonly IJSUnmarshalledRuntime _invoker; - // For unit testing. - internal WebAssemblyCultureProvider(IJSUnmarshalledRuntime invoker, CultureInfo initialCulture, CultureInfo initialUICulture) + internal WebAssemblyCultureProvider(CultureInfo initialCulture, CultureInfo initialUICulture) { - _invoker = invoker; InitialCulture = initialCulture; InitialUICulture = initialUICulture; } @@ -38,7 +33,6 @@ internal WebAssemblyCultureProvider(IJSUnmarshalledRuntime invoker, CultureInfo internal static void Initialize() { Instance = new WebAssemblyCultureProvider( - DefaultWebAssemblyJSRuntime.Instance, initialCulture: CultureInfo.CurrentCulture, initialUICulture: CultureInfo.CurrentUICulture); } diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs index 11f07020f3e6..b2beee5c220e 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs @@ -45,10 +45,9 @@ public static WebAssemblyHostBuilder CreateDefault(string[]? args = default) { // We don't use the args for anything right now, but we want to accept them // here so that it shows up this way in the project templates. - var jsRuntime = DefaultWebAssemblyJSRuntime.Instance; var builder = new WebAssemblyHostBuilder( - jsRuntime, - jsRuntime.ReadJsonSerializerOptions()); + InternalJSImportMethods.Instance, + DefaultWebAssemblyJSRuntime.Instance.ReadJsonSerializerOptions()); WebAssemblyCultureProvider.Initialize(); @@ -62,7 +61,9 @@ public static WebAssemblyHostBuilder CreateDefault(string[]? args = default) /// /// Creates an instance of with the minimal configuration. /// - internal WebAssemblyHostBuilder(IJSUnmarshalledRuntime jsRuntime, JsonSerializerOptions jsonOptions) + internal WebAssemblyHostBuilder( + IInternalJSImportMethods jsMethods, + JsonSerializerOptions jsonOptions) { // Private right now because we don't have much reason to expose it. This can be exposed // in the future if we want to give people a choice between CreateDefault and something @@ -74,12 +75,12 @@ internal WebAssemblyHostBuilder(IJSUnmarshalledRuntime jsRuntime, JsonSerializer Logging = new LoggingBuilder(Services); // Retrieve required attributes from JSRuntimeInvoker - InitializeNavigationManager(jsRuntime); - InitializeRegisteredRootComponents(jsRuntime); - InitializePersistedState(jsRuntime); + InitializeNavigationManager(jsMethods); + InitializeRegisteredRootComponents(jsMethods); + InitializePersistedState(jsMethods); InitializeDefaultServices(); - var hostEnvironment = InitializeEnvironment(jsRuntime); + var hostEnvironment = InitializeEnvironment(jsMethods); HostEnvironment = hostEnvironment; _createServiceProvider = () => @@ -89,10 +90,9 @@ internal WebAssemblyHostBuilder(IJSUnmarshalledRuntime jsRuntime, JsonSerializer } [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = "Root components are expected to be defined in assemblies that do not get trimmed.")] - private void InitializeRegisteredRootComponents(IJSUnmarshalledRuntime jsRuntime) + private void InitializeRegisteredRootComponents(IInternalJSImportMethods jsMethods) { -#pragma warning disable CS0618 // Type or member is obsolete - var componentsCount = jsRuntime.InvokeUnmarshalled(RegisteredComponentsInterop.GetRegisteredComponentsCount); + var componentsCount = jsMethods.RegisteredComponents_GetRegisteredComponentsCount(); if (componentsCount == 0) { return; @@ -101,14 +101,13 @@ private void InitializeRegisteredRootComponents(IJSUnmarshalledRuntime jsRuntime var registeredComponents = new WebAssemblyComponentMarker[componentsCount]; for (var i = 0; i < componentsCount; i++) { - var id = jsRuntime.InvokeUnmarshalled(RegisteredComponentsInterop.GetId, i); - var assembly = jsRuntime.InvokeUnmarshalled(RegisteredComponentsInterop.GetAssembly, id); - var typeName = jsRuntime.InvokeUnmarshalled(RegisteredComponentsInterop.GetTypeName, id); - var serializedParameterDefinitions = jsRuntime.InvokeUnmarshalled(RegisteredComponentsInterop.GetParameterDefinitions, id, null, null); - var serializedParameterValues = jsRuntime.InvokeUnmarshalled(RegisteredComponentsInterop.GetParameterValues, id, null, null); + var id = jsMethods.RegisteredComponents_GetId(i); + var assembly = jsMethods.RegisteredComponents_GetAssembly(id); + var typeName = jsMethods.RegisteredComponents_GetTypeName(id); + var serializedParameterDefinitions = jsMethods.RegisteredComponents_GetParameterDefinitions(id); + var serializedParameterValues = jsMethods.RegisteredComponents_GetParameterValues(id); registeredComponents[i] = new WebAssemblyComponentMarker(WebAssemblyComponentMarker.ClientMarkerType, assembly, typeName, serializedParameterDefinitions, serializedParameterValues, id.ToString(CultureInfo.InvariantCulture)); } -#pragma warning restore CS0618 // Type or member is obsolete var componentDeserializer = WebAssemblyComponentParameterDeserializer.Instance; foreach (var registeredComponent in registeredComponents) @@ -130,45 +129,35 @@ private void InitializeRegisteredRootComponents(IJSUnmarshalledRuntime jsRuntime } } - private void InitializePersistedState(IJSUnmarshalledRuntime jsRuntime) + private void InitializePersistedState(IInternalJSImportMethods jsMethods) { -#pragma warning disable CS0618 // Type or member is obsolete - _persistedState = jsRuntime.InvokeUnmarshalled("Blazor._internal.getPersistedState"); -#pragma warning restore CS0618 // Type or member is obsolete + _persistedState = jsMethods.GetPersistedState(); } - private static void InitializeNavigationManager(IJSUnmarshalledRuntime jsRuntime) + private static void InitializeNavigationManager(IInternalJSImportMethods jsMethods) { -#pragma warning disable CS0618 // Type or member is obsolete - var baseUri = jsRuntime.InvokeUnmarshalled(BrowserNavigationManagerInterop.GetBaseUri); - var uri = jsRuntime.InvokeUnmarshalled(BrowserNavigationManagerInterop.GetLocationHref); -#pragma warning restore CS0618 // Type or member is obsolete + var baseUri = jsMethods.NavigationManager_GetBaseUri(); + var uri = jsMethods.NavigationManager_GetLocationHref(); WebAssemblyNavigationManager.Instance = new WebAssemblyNavigationManager(baseUri, uri); } - private WebAssemblyHostEnvironment InitializeEnvironment(IJSUnmarshalledRuntime jsRuntime) + private WebAssemblyHostEnvironment InitializeEnvironment(IInternalJSImportMethods jsMethods) { -#pragma warning disable CS0618 // Type or member is obsolete - var applicationEnvironment = jsRuntime.InvokeUnmarshalled("Blazor._internal.getApplicationEnvironment"); -#pragma warning restore CS0618 // Type or member is obsolete + var applicationEnvironment = jsMethods.GetApplicationEnvironment(); var hostEnvironment = new WebAssemblyHostEnvironment(applicationEnvironment, WebAssemblyNavigationManager.Instance.BaseUri); Services.AddSingleton(hostEnvironment); var configFiles = new[] { - "appsettings.json", - $"appsettings.{applicationEnvironment}.json" - }; + "appsettings.json", + $"appsettings.{applicationEnvironment}.json" + }; foreach (var configFile in configFiles) { -#pragma warning disable CS0618 // Type or member is obsolete - var appSettingsJson = jsRuntime.InvokeUnmarshalled( - "Blazor._internal.getConfig", configFile); -#pragma warning restore CS0618 // Type or member is obsolete - + var appSettingsJson = jsMethods.GetConfig(configFile); if (appSettingsJson != null) { // Perf: Using this over AddJsonStream. This allows the linker to trim out the "File"-specific APIs and assemblies diff --git a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs index 30c93aa9a7b2..2bc8633b3acf 100644 --- a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Globalization; using System.Reflection; -using Microsoft.AspNetCore.Components.WebAssembly.Services; +using System.Runtime.InteropServices.JavaScript; using Microsoft.Extensions.HotReload; using Microsoft.JSInterop; @@ -16,8 +16,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.HotReload; /// code. /// [EditorBrowsable(EditorBrowsableState.Never)] -public static class WebAssemblyHotReload +public static partial class WebAssemblyHotReload { + private const string BlazorHotReloadModuleName = "blazor-hotreload"; + private static HotReloadAgent? _hotReloadAgent; private static readonly UpdateDelta[] _updateDeltas = new[] { @@ -26,16 +28,14 @@ public static class WebAssemblyHotReload internal static async Task InitializeAsync() { - if (Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true") + if (Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true" && + OperatingSystem.IsBrowser()) { // Attempt to read previously applied hot reload deltas if the ASP.NET Core browser tools are available (indicated by the presence of the Environment variable). - // The agent is injected in to the hosted app and can serve this script that can provide results from local-storage . + // The agent is injected in to the hosted app and can serve this script that can provide results from local-storage. // See https://github.com/dotnet/aspnetcore/issues/37357#issuecomment-941237000 - - var jsObjectReference = (IJSUnmarshalledObjectReference)(await DefaultWebAssemblyJSRuntime.Instance.InvokeAsync("import", "/_framework/blazor-hotreload.js")); -#pragma warning disable CS0618 // Type or member is obsolete - await jsObjectReference.InvokeUnmarshalled>("receiveHotReload"); -#pragma warning restore CS0618 // Type or member is obsolete + await JSHost.ImportAsync(BlazorHotReloadModuleName, "/_framework/blazor-hotreload.js"); + ReceiveHotReload(); } } @@ -73,4 +73,7 @@ public static string GetApplyUpdateCapabilities() } return (string)method.Invoke(obj: null, parameters: null)!; } + + [JSImport("receiveHotReload", BlazorHotReloadModuleName)] + private static partial void ReceiveHotReload(); } diff --git a/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs b/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs index 0481c89aadb8..229c42407015 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices.JavaScript; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.Web.Infrastructure; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; @@ -81,14 +83,20 @@ protected override void ProcessPendingRender() private void CallBaseProcessPendingRender() => base.ProcessPendingRender(); /// - protected override Task UpdateDisplayAsync(in RenderBatch batch) + protected override unsafe Task UpdateDisplayAsync(in RenderBatch batch) { -#pragma warning disable CS0618 // Type or member is obsolete - DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled( - "Blazor._internal.renderBatch", - RendererId, - batch); -#pragma warning restore CS0618 // Type or member is obsolete + // This is a GC hazard - it would be ideal to pin 'batch' and all its contents to prevent + // it from getting moved, or pause the GC for the duration of the 'RenderBatch()' call. + // The key mitigation is that the JS-side code always processes renderbatches synchronously + // and never calls back into .NET during that process, so GC cannot run (assuming it would + // only run on the current thread). + // As an early-warning system in case we accidentally introduce bugs and violate that rule, + // or for edge cases where user code can be invoked during rendering (e.g., DOM mutation + // observers) we further enforce it on the JS side using a notion of "locking the heap" + // during rendering, which prevents any JS-to-.NET calls that go through Blazor APIs such + // as DotNet.invokeMethod or event handlers. + var batchCopy = batch; + RenderBatch(RendererId, Unsafe.AsPointer(ref batchCopy)); if (WebAssemblyCallQueue.HasUnstartedWork) { @@ -129,4 +137,7 @@ private static partial class Log [LoggerMessage(100, LogLevel.Critical, "Unhandled exception rendering component: {Message}", EventName = "ExceptionRenderingComponent")] public static partial void UnhandledExceptionRenderingComponent(ILogger logger, string message, Exception exception); } + + [JSImport("Blazor._internal.renderBatch", "blazor-internal")] + private static unsafe partial void RenderBatch(int id, void* batch); } diff --git a/src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs b/src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs index 16ec802fbc36..7a61adf94bc9 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs @@ -3,6 +3,8 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.InteropServices.JavaScript; +using System.Runtime.Versioning; using System.Text.Json; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.JSInterop; @@ -11,7 +13,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services; -internal sealed class DefaultWebAssemblyJSRuntime : WebAssemblyJSRuntime +internal sealed partial class DefaultWebAssemblyJSRuntime : WebAssemblyJSRuntime { internal static readonly DefaultWebAssemblyJSRuntime Instance = new(); @@ -20,7 +22,7 @@ internal sealed class DefaultWebAssemblyJSRuntime : WebAssemblyJSRuntime [DynamicDependency(nameof(InvokeDotNet))] [DynamicDependency(nameof(EndInvokeJS))] [DynamicDependency(nameof(BeginInvokeDotNet))] - [DynamicDependency(nameof(NotifyByteArrayAvailable))] + [DynamicDependency(nameof(ReceiveByteArrayFromJS))] private DefaultWebAssemblyJSRuntime() { ElementReferenceContext = new WebElementReferenceContext(this); @@ -29,14 +31,20 @@ private DefaultWebAssemblyJSRuntime() public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions; - // The following methods are invoke via Mono's JS interop mechanism (invoke_method) - public static string? InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) + [JSExport] + [SupportedOSPlatform("browser")] + public static string? InvokeDotNet( + string? assemblyName, + string methodIdentifier, + [JSMarshalAs] long dotNetObjectId, + string argsJson) { - var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId, CultureInfo.InvariantCulture), callId: null); + var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId: null); return DotNetDispatcher.Invoke(Instance, callInfo, argsJson); } - // Invoked via Mono's JS interop mechanism (invoke_method) + [JSExport] + [SupportedOSPlatform("browser")] public static void EndInvokeJS(string argsJson) { WebAssemblyCallQueue.Schedule(argsJson, static argsJson => @@ -47,8 +55,9 @@ public static void EndInvokeJS(string argsJson) }); } - // Invoked via Mono's JS interop mechanism (invoke_method) - public static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) + [JSExport] + [SupportedOSPlatform("browser")] + public static void BeginInvokeDotNet(string? callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) { // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID // We only need one for any given call. This helps to work around the limitation that we can @@ -75,21 +84,10 @@ public static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetO }); } - /// - /// Invoked via Mono's JS interop mechanism (invoke_method) - /// - /// Notifies .NET of an array that's available for transfer from JS to .NET - /// - /// Ideally that byte array would be transferred directly as a parameter on this - /// call, however that's not currently possible due to: . - /// - /// Id of the byte array - public static void NotifyByteArrayAvailable(int id) + [JSExport] + [SupportedOSPlatform("browser")] + private static void ReceiveByteArrayFromJS(int id, byte[] data) { -#pragma warning disable CS0618 // Type or member is obsolete - var data = Instance.InvokeUnmarshalled("Blazor._internal.retrieveByteArray"); -#pragma warning restore CS0618 // Type or member is obsolete - DotNetDispatcher.ReceiveByteArray(Instance, id, data); } diff --git a/src/Components/WebAssembly/WebAssembly/src/Services/IInternalJSImportMethods.cs b/src/Components/WebAssembly/WebAssembly/src/Services/IInternalJSImportMethods.cs new file mode 100644 index 000000000000..82a121cd1871 --- /dev/null +++ b/src/Components/WebAssembly/WebAssembly/src/Services/IInternalJSImportMethods.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Components.WebAssembly.Services; + +internal interface IInternalJSImportMethods +{ + string GetPersistedState(); + + string GetApplicationEnvironment(); + + byte[]? GetConfig(string configFile); + + void NavigationManager_EnableNavigationInterception(); + + string NavigationManager_GetLocationHref(); + + string NavigationManager_GetBaseUri(); + + void NavigationManager_SetHasLocationChangingListeners(bool value); + + int RegisteredComponents_GetRegisteredComponentsCount(); + + int RegisteredComponents_GetId(int index); + + string RegisteredComponents_GetAssembly(int id); + + string RegisteredComponents_GetTypeName(int id); + + string RegisteredComponents_GetParameterDefinitions(int id); + + string RegisteredComponents_GetParameterValues(int id); +} diff --git a/src/Components/WebAssembly/WebAssembly/src/Services/InternalJSImportMethods.cs b/src/Components/WebAssembly/WebAssembly/src/Services/InternalJSImportMethods.cs new file mode 100644 index 000000000000..29ad3a881328 --- /dev/null +++ b/src/Components/WebAssembly/WebAssembly/src/Services/InternalJSImportMethods.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices.JavaScript; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +namespace Microsoft.AspNetCore.Components.WebAssembly.Services; + +internal partial class InternalJSImportMethods : IInternalJSImportMethods +{ + public static readonly InternalJSImportMethods Instance = new(); + + private InternalJSImportMethods() { } + + public string GetPersistedState() + => GetPersistedStateCore(); + + public string GetApplicationEnvironment() + => GetApplicationEnvironmentCore(); + + public byte[]? GetConfig(string configFile) + => GetConfigCore(configFile); + + public void NavigationManager_EnableNavigationInterception() + => NavigationManager_EnableNavigationInterceptionCore(); + + public string NavigationManager_GetLocationHref() + => NavigationManager_GetLocationHrefCore(); + + public string NavigationManager_GetBaseUri() + => NavigationManager_GetBaseUriCore(); + + public void NavigationManager_SetHasLocationChangingListeners(bool value) + => NavigationManager_SetHasLocationChangingListenersCore(value); + + public int RegisteredComponents_GetRegisteredComponentsCount() + => RegisteredComponents_GetRegisteredComponentsCountCore(); + + public int RegisteredComponents_GetId(int index) + => RegisteredComponents_GetIdCore(index); + + public string RegisteredComponents_GetAssembly(int id) + => RegisteredComponents_GetAssemblyCore(id); + + public string RegisteredComponents_GetTypeName(int id) + => RegisteredComponents_GetTypeNameCore(id); + + public string RegisteredComponents_GetParameterDefinitions(int id) + => RegisteredComponents_GetParameterDefinitionsCore(id); + + public string RegisteredComponents_GetParameterValues(int id) + => RegisteredComponents_GetParameterValuesCore(id); + + [JSImport("Blazor._internal.getPersistedState", "blazor-internal")] + private static partial string GetPersistedStateCore(); + + [JSImport("Blazor._internal.getApplicationEnvironment", "blazor-internal")] + private static partial string GetApplicationEnvironmentCore(); + + [JSImport("Blazor._internal.getConfig", "blazor-internal")] + private static partial byte[] GetConfigCore(string configFile); + + [JSImport(BrowserNavigationManagerInterop.EnableNavigationInterception, "blazor-internal")] + private static partial void NavigationManager_EnableNavigationInterceptionCore(); + + [JSImport(BrowserNavigationManagerInterop.GetLocationHref, "blazor-internal")] + private static partial string NavigationManager_GetLocationHrefCore(); + + [JSImport(BrowserNavigationManagerInterop.GetBaseUri, "blazor-internal")] + private static partial string NavigationManager_GetBaseUriCore(); + + [JSImport(BrowserNavigationManagerInterop.SetHasLocationChangingListeners, "blazor-internal")] + private static partial void NavigationManager_SetHasLocationChangingListenersCore(bool value); + + [JSImport(RegisteredComponentsInterop.GetRegisteredComponentsCount, "blazor-internal")] + private static partial int RegisteredComponents_GetRegisteredComponentsCountCore(); + + [JSImport(RegisteredComponentsInterop.GetId, "blazor-internal")] + private static partial int RegisteredComponents_GetIdCore(int index); + + [JSImport(RegisteredComponentsInterop.GetAssembly, "blazor-internal")] + private static partial string RegisteredComponents_GetAssemblyCore(int id); + + [JSImport(RegisteredComponentsInterop.GetTypeName, "blazor-internal")] + private static partial string RegisteredComponents_GetTypeNameCore(int id); + + [JSImport(RegisteredComponentsInterop.GetParameterDefinitions, "blazor-internal")] + private static partial string RegisteredComponents_GetParameterDefinitionsCore(int id); + + [JSImport(RegisteredComponentsInterop.GetParameterValues, "blazor-internal")] + private static partial string RegisteredComponents_GetParameterValuesCore(int id); +} diff --git a/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationInterception.cs b/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationInterception.cs index 31e8308eecaf..15255de94ba3 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationInterception.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationInterception.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components.Routing; -using Microsoft.JSInterop; -using Interop = Microsoft.AspNetCore.Components.Web.BrowserNavigationManagerInterop; namespace Microsoft.AspNetCore.Components.WebAssembly.Services; @@ -13,7 +11,7 @@ internal sealed class WebAssemblyNavigationInterception : INavigationInterceptio public Task EnableNavigationInterceptionAsync() { - DefaultWebAssemblyJSRuntime.Instance.InvokeVoid(Interop.EnableNavigationInterception); + InternalJSImportMethods.Instance.NavigationManager_EnableNavigationInterception(); return Task.CompletedTask; } } diff --git a/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationManager.cs b/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationManager.cs index 6fedfe37aeb8..ad797ee20be9 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationManager.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationManager.cs @@ -85,7 +85,7 @@ protected override void HandleLocationChangingHandlerException(Exception ex, Loc } protected override void SetNavigationLockState(bool value) - => DefaultWebAssemblyJSRuntime.Instance.InvokeVoid(Interop.SetHasLocationChangingListeners, value); + => InternalJSImportMethods.Instance.NavigationManager_SetHasLocationChangingListeners(value); private static partial class Log { diff --git a/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs b/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs index 50d798e01cf3..0c530d8ddbb4 100644 --- a/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs +++ b/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs @@ -32,7 +32,7 @@ public void ThrowIfCultureChangeIsUnsupported_ThrowsIfCulturesAreDifferentAndICU try { // WebAssembly is initialized with en-US - var cultureProvider = new WebAssemblyCultureProvider(DefaultWebAssemblyJSRuntime.Instance, new CultureInfo("en-US"), new CultureInfo("en-US")); + var cultureProvider = new WebAssemblyCultureProvider(new CultureInfo("en-US"), new CultureInfo("en-US")); // Culture is changed to fr-FR as part of the app using var cultureReplacer = new CultureReplacer("fr-FR"); diff --git a/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs b/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs index b6a6e62c4f3b..0e6f259e7c72 100644 --- a/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs +++ b/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs @@ -20,12 +20,12 @@ public class WebAssemblyHostBuilderTest public void Build_AllowsConfiguringConfiguration() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Configuration.AddInMemoryCollection(new[] { - new KeyValuePair("key", "value"), - }); + new KeyValuePair("key", "value"), + }); // Act var host = builder.Build(); @@ -38,7 +38,7 @@ public void Build_AllowsConfiguringConfiguration() public void Build_AllowsConfiguringServices() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); // This test also verifies that we create a scope. builder.Services.AddScoped(); @@ -54,7 +54,7 @@ public void Build_AllowsConfiguringServices() public void Build_AllowsConfiguringContainer() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddScoped(); var factory = new MyFakeServiceProviderFactory(); @@ -72,7 +72,7 @@ public void Build_AllowsConfiguringContainer() public void Build_AllowsConfiguringContainer_WithDelegate() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddScoped(); @@ -95,7 +95,7 @@ public void Build_AllowsConfiguringContainer_WithDelegate() public void Build_InDevelopment_ConfiguresWithServiceProviderWithScopeValidation() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(environment: "Development"), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(environment: "Development"), JsonOptions); builder.Services.AddScoped(); builder.Services.AddSingleton(); @@ -112,7 +112,7 @@ public void Build_InDevelopment_ConfiguresWithServiceProviderWithScopeValidation public void Build_InProduction_ConfiguresWithServiceProviderWithScopeValidation() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddScoped(); builder.Services.AddSingleton(); @@ -129,7 +129,7 @@ public void Build_InProduction_ConfiguresWithServiceProviderWithScopeValidation( public void Builder_InDevelopment_SetsHostEnvironmentProperty() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(environment: "Development"), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(environment: "Development"), JsonOptions); // Assert Assert.NotNull(builder.HostEnvironment); @@ -140,7 +140,7 @@ public void Builder_InDevelopment_SetsHostEnvironmentProperty() public void Builder_CreatesNavigationManager() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(environment: "Development"), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(environment: "Development"), JsonOptions); // Act var host = builder.Build(); @@ -190,7 +190,7 @@ public IServiceProvider CreateServiceProvider(MyFakeDIBuilderThing containerBuil public void Build_AddsConfigurationToServices() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Configuration.AddInMemoryCollection(new[] { @@ -225,7 +225,7 @@ private static IReadOnlyList DefaultServiceTypes public void Constructor_AddsDefaultServices() { // Arrange & Act - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); foreach (var type in DefaultServiceTypes) { @@ -237,7 +237,7 @@ public void Constructor_AddsDefaultServices() public void Builder_SupportsConfiguringLogging() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var provider = new Mock(); // Act diff --git a/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs b/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs index 1fec01fe1137..b4a35e0f7a73 100644 --- a/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs +++ b/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs @@ -19,7 +19,7 @@ public class WebAssemblyHostTest public async Task RunAsync_CanExitBasedOnCancellationToken() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var host = builder.Build(); var cultureProvider = new TestSatelliteResourcesLoader(); @@ -38,7 +38,7 @@ public async Task RunAsync_CanExitBasedOnCancellationToken() public async Task RunAsync_CallingTwiceCausesException() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); var host = builder.Build(); var cultureProvider = new TestSatelliteResourcesLoader(); @@ -59,7 +59,7 @@ public async Task RunAsync_CallingTwiceCausesException() public async Task DisposeAsync_CanDisposeAfterCallingRunAsync() { // Arrange - var builder = new WebAssemblyHostBuilder(new TestJSUnmarshalledRuntime(), JsonOptions); + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(), JsonOptions); builder.Services.AddSingleton(); var host = builder.Build(); var cultureProvider = new TestSatelliteResourcesLoader(); @@ -95,7 +95,7 @@ public ValueTask DisposeAsync() private class TestSatelliteResourcesLoader : WebAssemblyCultureProvider { internal TestSatelliteResourcesLoader() - : base(DefaultWebAssemblyJSRuntime.Instance, CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture) + : base(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture) { } diff --git a/src/Components/WebAssembly/WebAssembly/test/TestInternalJSImportMethods.cs b/src/Components/WebAssembly/WebAssembly/test/TestInternalJSImportMethods.cs new file mode 100644 index 000000000000..e76e5af67374 --- /dev/null +++ b/src/Components/WebAssembly/WebAssembly/test/TestInternalJSImportMethods.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Components.WebAssembly.Services; + +namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +internal sealed class TestInternalJSImportMethods : IInternalJSImportMethods +{ + private readonly string _environment; + + public TestInternalJSImportMethods(string environment = "Production") + { + _environment = environment; + } + + public string GetApplicationEnvironment() + => _environment; + + public byte[] GetConfig(string configFile) + => null; + + public string GetPersistedState() + => null; + + public void NavigationManager_EnableNavigationInterception() { } + + public string NavigationManager_GetBaseUri() + => "https://www.example.com/awesome-part-that-will-be-truncated-in-tests"; + + public string NavigationManager_GetLocationHref() + => "https://www.example.com/awesome-part-that-will-be-truncated-in-tests/cool"; + + public void NavigationManager_SetHasLocationChangingListeners(bool value) { } + + public string RegisteredComponents_GetAssembly(int id) + => string.Empty; + + public int RegisteredComponents_GetId(int index) + => 0; + + public string RegisteredComponents_GetParameterDefinitions(int id) + => string.Empty; + + public string RegisteredComponents_GetParameterValues(int id) + => string.Empty; + + public int RegisteredComponents_GetRegisteredComponentsCount() + => 0; + + public string RegisteredComponents_GetTypeName(int id) + => string.Empty; +} diff --git a/src/Components/WebAssembly/WebAssembly/test/TestJSUnmarshalledRuntime.cs b/src/Components/WebAssembly/WebAssembly/test/TestJSUnmarshalledRuntime.cs deleted file mode 100644 index 8f8f1763c67f..000000000000 --- a/src/Components/WebAssembly/WebAssembly/test/TestJSUnmarshalledRuntime.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.JSInterop; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting; - -internal class TestJSUnmarshalledRuntime : IJSUnmarshalledRuntime -{ - private readonly string _environment; - - public TestJSUnmarshalledRuntime(string environment = "Production") - { - _environment = environment; - } - - public TResult InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) - { - switch (identifier) - { - case "Blazor._internal.getApplicationEnvironment": - return (TResult)(object)_environment; - case "Blazor._internal.getConfig": - return (TResult)(object)null; - case "Blazor._internal.navigationManager.getUnmarshalledBaseURI": - var testUri = "https://www.example.com/awesome-part-that-will-be-truncated-in-tests"; - return (TResult)(object)testUri; - case "Blazor._internal.navigationManager.getUnmarshalledLocationHref": - var testHref = "https://www.example.com/awesome-part-that-will-be-truncated-in-tests/cool"; - return (TResult)(object)testHref; - case "Blazor._internal.registeredComponents.getRegisteredComponentsCount": - return (TResult)(object)0; - case "Blazor._internal.getPersistedState": - return (TResult)(object)null; - default: - throw new NotImplementedException($"{nameof(TestJSUnmarshalledRuntime)} has no implementation for '{identifier}'."); - } - } - - public TResult InvokeUnmarshalled(string identifier) - => InvokeUnmarshalled(identifier, null, null, null); - - public TResult InvokeUnmarshalled(string identifier, T0 arg0) - => InvokeUnmarshalled(identifier, arg0, null, null); - - public TResult InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) - => InvokeUnmarshalled(identifier, arg0, arg1, null); -} diff --git a/src/Components/test/E2ETest/Tests/InteropTest.cs b/src/Components/test/E2ETest/Tests/InteropTest.cs index 66363c1e540d..41c5c7fa5e79 100644 --- a/src/Components/test/E2ETest/Tests/InteropTest.cs +++ b/src/Components/test/E2ETest/Tests/InteropTest.cs @@ -114,9 +114,9 @@ public void CanInvokeDotNetMethods() ["result8"] = @"[{""id"":7,""isValid"":false,""data"":{""source"":""Some random text with at least 7 characters"",""start"":7,""length"":7}},7,123,28,56,7.25,[0.5,1.5,2.5,3.5,4.5,5.5,6.5]]", ["result9"] = @"[{""id"":8,""isValid"":true,""data"":{""source"":""Some random text with at least 8 characters"",""start"":8,""length"":8}},8,123,32,64,8.25,[0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5],{""source"":""Some random text with at least 7 characters"",""start"":9,""length"":9}]", ["roundTripJSObjectReference"] = @"""successful""", - ["invokeDisposedJSObjectReferenceException"] = @"""JS object instance with ID", - ["ThrowException"] = @"""System.InvalidOperationException: Threw an exception!", - ["ExceptionFromSyncMethod"] = "Function threw an exception!", + ["invokeDisposedJSObjectReferenceException"] = @"""Error: JS object instance with ID", + ["ThrowException"] = @"""Threw an exception!", + ["ExceptionFromSyncMethod"] = "Error: Function threw an exception!", ["roundTripByteArrayFromJS"] = @"{""0"":1,""1"":5,""2"":7,""3"":17,""4"":200,""5"":138}", ["roundTripByteArrayWrapperObjectFromJS"] = @"{""strVal"":""Some string"",""byteArrayVal"":{""0"":1,""1"":5,""2"":7,""3"":17,""4"":200,""5"":138},""intVal"":42}", ["roundTripByteArrayFromDotNet"] = @"1,5,7,15,35,200", diff --git a/src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs index ea5ba5bf13ab..5764e5fc3eb5 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices.JavaScript; using static Microsoft.AspNetCore.Internal.LinkerFlags; namespace Microsoft.JSInterop.Implementation; @@ -9,7 +10,7 @@ namespace Microsoft.JSInterop.Implementation; /// /// Implements functionality for . /// -public class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference +public partial class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference { private readonly JSInProcessRuntime _jsRuntime; @@ -39,7 +40,10 @@ public void Dispose() { Disposed = true; - _jsRuntime.InvokeVoid("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id); + DisposeJSObjectReferenceById(Id); } } + + [JSImport("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", "blazor-internal")] + private static partial void DisposeJSObjectReferenceById([JSMarshalAs] long id); } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index a278b72d34d4..baebaf34e3c8 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -9,6 +9,7 @@ enable true $(DefineConstants);JS_INTEROP + true false