diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs index 391f692e0baf08..45e23cf2e48111 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs @@ -21,6 +21,15 @@ public static void CancelPromise(Task promise) GCHandle? promiseGCHandle = promise.AsyncState as GCHandle?; if (promiseGCHandle == null) throw new InvalidOperationException("Expected Task converted from JS Promise"); +#if FEATURE_WASM_THREADS + // TODO JSObject.AssertThreadAffinity(promise); + // in order to remember the thread ID of the promise, we would have to allocate holder object for any Task, + // which would hold thread ID and the GCHandle + // that would be pretty expensive, so we don't do it for now + // the consequences are that calling CancelPromise on wrong thread would do nothing + // because there would not be any object on JS registered under the same GCHandle + // perhaps that's the point when we could throw an exception on JS side. +#endif _CancelPromise((IntPtr)promiseGCHandle.Value); } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs index 10092d5971bb4f..38605743fad931 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs @@ -71,6 +71,10 @@ public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int should public static void CreateCSOwnedProxyRef(nint jsHandle, LegacyHostImplementation.MappedType mappedType, int shouldAddInflight, out JSObject jsObject) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif + JSObject? res = null; lock (JSHostImplementation.s_csOwnedObjects) @@ -144,6 +148,9 @@ public static void GetTaskSourceTaskRef(int tcsGCHandle, out object result) public static void SetupJSContinuationRef(in Task _task, JSObject continuationObj) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif // HACK: Attempting to use the in-param will produce CS1628, so we make a temporary copy // on the stack that can be captured by our local functions below var task = _task; @@ -225,6 +232,9 @@ public static void CreateDateTimeRef(double ticks, out object result) [Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2057", Justification = "Done on purpose, see comment above.")] public static void CreateUriRef(string uri, out object? result) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif if (uriType == null) { // StringBuilder to confuse ILLink, which is too smart otherwise diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs index c01965dc4610c3..8d415f7201e5c4 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs @@ -172,6 +172,10 @@ public static JSFunctionBinding BindManagedFunction(string fullyQualifiedName, i [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe void InvokeJSImpl(JSObject jsFunction, Span arguments) { +#if FEATURE_WASM_THREADS + JSObject.AssertThreadAffinity(jsFunction); +#endif + IntPtr functionJSHandle = jsFunction.JSHandle; fixed (JSMarshalerArgument* ptr = arguments) { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs index d731a48392b1e1..6cc22a1f254e65 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Threading; namespace System.Runtime.InteropServices.JavaScript { @@ -10,6 +11,10 @@ public partial class JSObject { internal nint JSHandle; +#if FEATURE_WASM_THREADS + // the JavaScript object could only exist on the single web worker and can't migrate to other workers + internal int OwnerThreadId; +#endif #if !DISABLE_LEGACY_JS_INTEROP internal GCHandle? InFlight; internal int InFlightCounter; @@ -19,6 +24,9 @@ public partial class JSObject internal JSObject(IntPtr jsHandle) { JSHandle = jsHandle; +#if FEATURE_WASM_THREADS + OwnerThreadId = Thread.CurrentThread.ManagedThreadId; +#endif } #if !DISABLE_LEGACY_JS_INTEROP @@ -56,6 +64,30 @@ internal void ReleaseInFlight() } #endif +#if FEATURE_WASM_THREADS + internal static void AssertThreadAffinity(object value) + { + if (value == null) + { + return; + } + if (value is JSObject jsObject) + { + if (jsObject.OwnerThreadId != Thread.CurrentThread.ManagedThreadId) + { + throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created."); + } + } + if (value is JSException jsException) + { + if(jsException.jsException!=null && jsException.jsException.OwnerThreadId != Thread.CurrentThread.ManagedThreadId) + { + throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created."); + } + } + } +#endif + /// public override bool Equals([NotNullWhen(true)] object? obj) => obj is JSObject other && JSHandle == other.JSHandle; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs index 987a2d2fd8bf0c..29ada438164ccf 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs @@ -18,6 +18,9 @@ public class Array : JSObject public Array(params object[] _params) : base(JavaScriptImports.CreateCSOwnedObject(nameof(Array), _params)) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs index e19dd5ffc6df8a..39db0563089d0d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs @@ -13,6 +13,9 @@ public class ArrayBuffer : JSObject public ArrayBuffer(int length) : base(JavaScriptImports.CreateCSOwnedObject(nameof(ArrayBuffer), new object[] { length })) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs index f43583e512a180..3d3300ec5c5039 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs @@ -17,6 +17,9 @@ public class DataView : JSObject public DataView(ArrayBuffer buffer) : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer })) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } @@ -28,6 +31,9 @@ public DataView(ArrayBuffer buffer) public DataView(ArrayBuffer buffer, int byteOffset) : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset })) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } @@ -40,6 +46,9 @@ public DataView(ArrayBuffer buffer, int byteOffset) public DataView(ArrayBuffer buffer, int byteOffset, int byteLength) : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset, byteLength })) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs index 2c778706b497e1..f87fb94c01610a 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs @@ -18,6 +18,9 @@ public class Function : JSObject public Function(params object[] args) : base(JavaScriptImports.CreateCSOwnedObject(nameof(Function), args)) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs index d24a1decdff638..6a5de6a03e5038 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading.Tasks; +using System.Threading; namespace System.Runtime.InteropServices.JavaScript { @@ -220,5 +221,15 @@ public enum MappedType Function = 4, Uint8Array = 11, } + +#if FEATURE_WASM_THREADS + public static void ThrowIfLegacyWorkerThread() + { + if (Thread.CurrentThread.ManagedThreadId != 1) + { + throw new PlatformNotSupportedException("Legacy interop is not supported with WebAssembly threads."); + } + } +#endif } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs index 13147c81020525..c6e0e362af65e7 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs @@ -34,6 +34,9 @@ public static object GetGlobalObject(string str) /// public static object Invoke(this JSObject self, string method, params object?[] args) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif ArgumentNullException.ThrowIfNull(self); ObjectDisposedException.ThrowIf(self.IsDisposed, self); Interop.Runtime.InvokeJSWithArgsRef(self.JSHandle, method, args, out int exception, out object res); @@ -68,6 +71,9 @@ public static object Invoke(this JSObject self, string method, params object?[] /// public static object GetObjectProperty(this JSObject self, string name) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif ArgumentNullException.ThrowIfNull(self); ObjectDisposedException.ThrowIf(self.IsDisposed, self); @@ -92,6 +98,9 @@ public static object GetObjectProperty(this JSObject self, string name) /// public static void SetObjectProperty(this JSObject self, string name, object? value, bool createIfNotExists = true, bool hasOwnProperty = false) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif ArgumentNullException.ThrowIfNull(self); ObjectDisposedException.ThrowIf(self.IsDisposed, self); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs index fb9c8dae9539dc..b80f7c31ad64b4 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs @@ -11,12 +11,18 @@ public sealed class Uint8Array : JSObject public Uint8Array(int length) : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { length })) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } public Uint8Array(ArrayBuffer buffer) : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { buffer })) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif LegacyHostImplementation.RegisterCSOwnedObject(this); } @@ -49,6 +55,9 @@ public byte[] ToArray() public static unsafe Uint8Array From(ReadOnlySpan span) { +#if FEATURE_WASM_THREADS + LegacyHostImplementation.ThrowIfLegacyWorkerThread(); +#endif // source has to be instantiated. if (span == null) { @@ -65,7 +74,6 @@ public static unsafe Uint8Array From(ReadOnlySpan span) r.ReleaseInFlight(); return r; } - } public enum TypedArrayTypeCode diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs index d847549a4a59d1..e19a3f428c8a23 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Exception.cs @@ -65,6 +65,9 @@ public unsafe void ToJS(Exception? value) var jse = cpy as JSException; if (jse != null && jse.jsException != null) { +#if FEATURE_WASM_THREADS + JSObject.AssertThreadAffinity(value); +#endif // this is JSException roundtrip ObjectDisposedException.ThrowIf(jse.jsException.IsDisposed, value); slot.Type = MarshalerType.JSException; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs index 2e9f155f3f75be..5717e8d2cee87a 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.JSObject.cs @@ -37,6 +37,9 @@ public void ToJS(JSObject? value) } else { +#if FEATURE_WASM_THREADS + JSObject.AssertThreadAffinity(value); +#endif ObjectDisposedException.ThrowIf(value.IsDisposed, value); slot.Type = MarshalerType.JSObject; slot.JSHandle = value.JSHandle; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs index 7d8d37b04c0269..3ebd4d7618fa47 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs @@ -228,6 +228,10 @@ public void ToJS(Task? value) void Complete() { +#if FEATURE_WASM_THREADS + JSObject.AssertThreadAffinity(promise); +#endif + // When this task was never resolved/rejected // promise (held by this lambda) would be collected by GC after the Task is collected // and would also allow the JS promise to be collected @@ -304,6 +308,9 @@ public void ToJS(Task? value, ArgumentToJSCallback marshaler) void Complete() { +#if FEATURE_WASM_THREADS + JSObject.AssertThreadAffinity(promise); +#endif // When this task was never resolved/rejected // promise (held by this lambda) would be collected by GC after the Task is collected // and would also allow the JS promise to be collected diff --git a/src/mono/wasm/runtime/net6-legacy/method-binding.ts b/src/mono/wasm/runtime/net6-legacy/method-binding.ts index 2eefa077fa2c56..a5614e2e82dc7b 100644 --- a/src/mono/wasm/runtime/net6-legacy/method-binding.ts +++ b/src/mono/wasm/runtime/net6-legacy/method-binding.ts @@ -1,8 +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 MonoWasmThreads from "consts:monoWasmThreads"; import { legacy_c_functions as cwraps } from "../cwraps"; -import { Module } from "../imports"; +import { ENVIRONMENT_IS_PTHREAD, Module } from "../imports"; import { parseFQN } from "../invoke-cs"; import { setI32, setU32, setF32, setF64, setU52, setI52, setB32, setI32_unchecked, setU32_unchecked, _zero_region, _create_temp_frame, getB32, getI32, getU32, getF32, getF64 } from "../memory"; import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; @@ -382,6 +383,9 @@ export function _decide_if_result_is_marshaled(converter: Converter, argc: numbe export function mono_bind_method(method: MonoMethod, args_marshal: string/*ArgsMarshalString*/, has_this_arg: boolean, friendly_name?: string): Function { + if (MonoWasmThreads && ENVIRONMENT_IS_PTHREAD) { + throw new Error("Legacy interop is not supported with WebAssembly threads."); + } if (typeof (args_marshal) !== "string") throw new Error("args_marshal argument invalid, expected string"); diff --git a/src/mono/wasm/runtime/net6-legacy/method-calls.ts b/src/mono/wasm/runtime/net6-legacy/method-calls.ts index f1a89755eb5c1f..fa50cb01d42416 100644 --- a/src/mono/wasm/runtime/net6-legacy/method-calls.ts +++ b/src/mono/wasm/runtime/net6-legacy/method-calls.ts @@ -1,8 +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 MonoWasmThreads from "consts:monoWasmThreads"; import { get_js_obj, mono_wasm_get_jsobj_from_js_handle } from "../gc-handles"; -import { Module, runtimeHelpers, INTERNAL } from "../imports"; +import { Module, runtimeHelpers, INTERNAL, ENVIRONMENT_IS_PTHREAD } from "../imports"; import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; import { _release_temp_frame } from "../memory"; import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; @@ -90,6 +91,9 @@ export function mono_call_assembly_entry_point(assembly: string, args?: any[], s } export function mono_wasm_invoke_js_with_args_ref(js_handle: JSHandle, method_name: MonoStringRef, args: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): any { + if (MonoWasmThreads && ENVIRONMENT_IS_PTHREAD) { + throw new Error("Legacy interop is not supported with WebAssembly threads."); + } const argsRoot = mono_wasm_new_external_root(args), nameRoot = mono_wasm_new_external_root(method_name), resultRoot = mono_wasm_new_external_root(result_address); @@ -282,6 +286,9 @@ export function mono_wasm_get_global_object_ref(global_name: MonoStringRef, is_e // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function mono_wasm_invoke_js_blazor(exceptionMessage: Int32Ptr, callInfo: any, arg0: any, arg1: any, arg2: any): void | number { try { + if (MonoWasmThreads) { + throw new Error("Legacy interop is not supported with WebAssembly threads."); + } const blazorExports = (globalThis).Blazor; if (!blazorExports) { throw new Error("The blazor.webassembly.js library is not loaded.");