From 0bc52b95737e23b8269f348940ca54f3c674c1a5 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 13 Jul 2023 18:21:18 +0200 Subject: [PATCH 1/3] detect and assert features --- src/mono/wasm/runtime/http.ts | 8 +++++++- src/mono/wasm/runtime/loader/index.ts | 3 +++ src/mono/wasm/runtime/loader/polyfills.ts | 17 +++++++++++++++++ src/mono/wasm/runtime/loader/worker.ts | 3 +++ src/mono/wasm/runtime/web-socket.ts | 11 ++++++++++- 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/runtime/http.ts b/src/mono/wasm/runtime/http.ts index 9034494a4615c..a2ca3800ef2e7 100644 --- a/src/mono/wasm/runtime/http.ts +++ b/src/mono/wasm/runtime/http.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { wrap_as_cancelable_promise } from "./cancelable-promise"; -import { Module, loaderHelpers } from "./globals"; +import { ENVIRONMENT_IS_NODE, Module, loaderHelpers } from "./globals"; import { MemoryViewType, Span } from "./marshal"; import type { VoidPtr } from "./types/emscripten"; @@ -11,6 +11,12 @@ export function http_wasm_supports_streaming_response(): boolean { } export function http_wasm_create_abort_controler(): AbortController { + if (typeof globalThis.fetch !== "function" || typeof globalThis.AbortController !== "function") { + const message = ENVIRONMENT_IS_NODE + ? "Please install fetch package to enable HTTP client support." + : "This browser doesn't support fetch API. Please use a modern browser."; + throw new Error(message); + } return new AbortController(); } diff --git a/src/mono/wasm/runtime/loader/index.ts b/src/mono/wasm/runtime/loader/index.ts index 9c2f0d00f181c..aa6b5160e4da0 100644 --- a/src/mono/wasm/runtime/loader/index.ts +++ b/src/mono/wasm/runtime/loader/index.ts @@ -3,6 +3,7 @@ import type { DotnetHostBuilder } from "../types"; import { mono_exit } from "./exit"; +import { sanityCheck } from "./polyfills"; import { HostBuilder, createEmscripten } from "./run"; // export external API @@ -10,5 +11,7 @@ const dotnet: DotnetHostBuilder = new HostBuilder(); const exit = mono_exit; const legacyEntrypoint = createEmscripten; +sanityCheck(); + export { dotnet, exit }; export default legacyEntrypoint; diff --git a/src/mono/wasm/runtime/loader/polyfills.ts b/src/mono/wasm/runtime/loader/polyfills.ts index 0b5e2be1967f8..1c7725863ae51 100644 --- a/src/mono/wasm/runtime/loader/polyfills.ts +++ b/src/mono/wasm/runtime/loader/polyfills.ts @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import MonoWasmThreads from "consts:monoWasmThreads"; import type { DotnetModuleInternal } from "../types/internal"; import { INTERNAL, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, ENVIRONMENT_IS_WEB } from "./globals"; @@ -5,6 +9,19 @@ import { INTERNAL, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, ENV let node_fs: any | undefined = undefined; let node_url: any | undefined = undefined; +export function sanityCheck() { + mono_assert(typeof globalThis.URL === "function", "This browser/engine doesn't support URL API. Please use a modern version."); + mono_assert(typeof globalThis.BigInt64Array === "function", "This browser/engine doesn't support BigInt64Array API. Please use a modern version."); + if (MonoWasmThreads) { + mono_assert(!ENVIRONMENT_IS_SHELL && !ENVIRONMENT_IS_NODE, "This build of dotnet is multi-threaded, it doesn't support shell environments like V8 or NodeJS."); + mono_assert(globalThis.SharedArrayBuffer !== undefined, "SharedArrayBuffer is not enabled on this page. Please use a modern browser and set Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy http headers. See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements"); + mono_assert(typeof globalThis.EventTarget === "function", "This browser/engine doesn't support EventTarget API. Please use a modern version."); + } + + // TODO detect other (WASM) features that are required for the runtime + // See https://github.com/dotnet/runtime/issues/84574 +} + export async function detect_features_and_polyfill(module: DotnetModuleInternal): Promise { const scriptUrlQuery =/* webpackIgnore: true */import.meta.url; diff --git a/src/mono/wasm/runtime/loader/worker.ts b/src/mono/wasm/runtime/loader/worker.ts index 2f2024140d8f4..a22c65fcb7881 100644 --- a/src/mono/wasm/runtime/loader/worker.ts +++ b/src/mono/wasm/runtime/loader/worker.ts @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { MonoConfig } from "../types"; import { MonoConfigInternal } from "../types/internal"; import { deep_merge_config, normalizeConfig } from "./config"; diff --git a/src/mono/wasm/runtime/web-socket.ts b/src/mono/wasm/runtime/web-socket.ts index 00ca6c8e70ebb..236c6037458c7 100644 --- a/src/mono/wasm/runtime/web-socket.ts +++ b/src/mono/wasm/runtime/web-socket.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { prevent_timer_throttling } from "./scheduling"; import { Queue } from "./queue"; -import { createPromiseController } from "./globals"; +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, createPromiseController } from "./globals"; import { setI32, localHeapViewU8 } from "./memory"; import { VoidPtr } from "./types/emscripten"; import { PromiseController } from "./types/internal"; @@ -27,6 +27,15 @@ const ws_send_buffer_blocking_threshold = 65536; const emptyBuffer = new Uint8Array(); export function ws_wasm_create(uri: string, sub_protocols: string[] | null, receive_status_ptr: VoidPtr, onClosed: (code: number, reason: string) => void): WebSocketExtension { + if (ENVIRONMENT_IS_SHELL) { + throw new Error("WebSockets are not supported in shell JS engine."); + } + if (typeof globalThis.WebSocket !== "function") { + const message = ENVIRONMENT_IS_NODE + ? "Please install WebSocket package to enable networking support." + : "This browser doesn't support WebSocket API. Please use a modern browser."; + throw new Error(message); + } mono_assert(uri && typeof uri === "string", () => `ERR12: Invalid uri ${typeof uri}`); const ws = new globalThis.WebSocket(uri, sub_protocols || undefined) as WebSocketExtension; From f20c3b52f01dfec88fe7020b68ed016452c07475 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 13 Jul 2023 18:59:33 +0200 Subject: [PATCH 2/3] fix --- src/mono/wasm/runtime/loader/polyfills.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/loader/polyfills.ts b/src/mono/wasm/runtime/loader/polyfills.ts index 3bda808f2d8f9..8afa95d134923 100644 --- a/src/mono/wasm/runtime/loader/polyfills.ts +++ b/src/mono/wasm/runtime/loader/polyfills.ts @@ -19,7 +19,7 @@ const URLPolyfill = class URL { }; export function sanityCheck() { - mono_assert(typeof globalThis.URL === "function", "This browser/engine doesn't support URL API. Please use a modern version."); + mono_assert(ENVIRONMENT_IS_SHELL || typeof globalThis.URL === "function", "This browser/engine doesn't support URL API. Please use a modern version."); mono_assert(typeof globalThis.BigInt64Array === "function", "This browser/engine doesn't support BigInt64Array API. Please use a modern version."); if (MonoWasmThreads) { mono_assert(!ENVIRONMENT_IS_SHELL && !ENVIRONMENT_IS_NODE, "This build of dotnet is multi-threaded, it doesn't support shell environments like V8 or NodeJS."); From 372fd7f14216d9cf0a2f04098cf7cb479cecffa9 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Jul 2023 12:27:38 +0200 Subject: [PATCH 3/3] feedback --- src/mono/wasm/runtime/http.ts | 16 +++++++++++----- src/mono/wasm/runtime/loader/index.ts | 4 ++-- src/mono/wasm/runtime/loader/polyfills.ts | 2 +- src/mono/wasm/runtime/web-socket.ts | 8 ++++++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/mono/wasm/runtime/http.ts b/src/mono/wasm/runtime/http.ts index a2ca3800ef2e7..ff9dbbbcfec1b 100644 --- a/src/mono/wasm/runtime/http.ts +++ b/src/mono/wasm/runtime/http.ts @@ -6,17 +6,22 @@ import { ENVIRONMENT_IS_NODE, Module, loaderHelpers } from "./globals"; import { MemoryViewType, Span } from "./marshal"; import type { VoidPtr } from "./types/emscripten"; -export function http_wasm_supports_streaming_response(): boolean { - return typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream === "function"; -} -export function http_wasm_create_abort_controler(): AbortController { +function verifyEnvironment() { if (typeof globalThis.fetch !== "function" || typeof globalThis.AbortController !== "function") { const message = ENVIRONMENT_IS_NODE - ? "Please install fetch package to enable HTTP client support." + ? "Please install `node-fetch` and `node-abort-controller` npm packages to enable HTTP client support." : "This browser doesn't support fetch API. Please use a modern browser."; throw new Error(message); } +} + +export function http_wasm_supports_streaming_response(): boolean { + return typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream === "function"; +} + +export function http_wasm_create_abort_controler(): AbortController { + verifyEnvironment(); return new AbortController(); } @@ -44,6 +49,7 @@ export function http_wasm_fetch_bytes(url: string, header_names: string[], heade } export function http_wasm_fetch(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: string | Uint8Array | null): Promise { + verifyEnvironment(); mono_assert(url && typeof url === "string", "expected url string"); mono_assert(header_names && header_values && Array.isArray(header_names) && Array.isArray(header_values) && header_names.length === header_values.length, "expected headerNames and headerValues arrays"); mono_assert(option_names && option_values && Array.isArray(option_names) && Array.isArray(option_values) && option_names.length === option_values.length, "expected headerNames and headerValues arrays"); diff --git a/src/mono/wasm/runtime/loader/index.ts b/src/mono/wasm/runtime/loader/index.ts index aa6b5160e4da0..6818186c36a02 100644 --- a/src/mono/wasm/runtime/loader/index.ts +++ b/src/mono/wasm/runtime/loader/index.ts @@ -3,7 +3,7 @@ import type { DotnetHostBuilder } from "../types"; import { mono_exit } from "./exit"; -import { sanityCheck } from "./polyfills"; +import { verifyEnvironment } from "./polyfills"; import { HostBuilder, createEmscripten } from "./run"; // export external API @@ -11,7 +11,7 @@ const dotnet: DotnetHostBuilder = new HostBuilder(); const exit = mono_exit; const legacyEntrypoint = createEmscripten; -sanityCheck(); +verifyEnvironment(); export { dotnet, exit }; export default legacyEntrypoint; diff --git a/src/mono/wasm/runtime/loader/polyfills.ts b/src/mono/wasm/runtime/loader/polyfills.ts index 8afa95d134923..cf4b5abdb5d10 100644 --- a/src/mono/wasm/runtime/loader/polyfills.ts +++ b/src/mono/wasm/runtime/loader/polyfills.ts @@ -18,7 +18,7 @@ const URLPolyfill = class URL { } }; -export function sanityCheck() { +export function verifyEnvironment() { mono_assert(ENVIRONMENT_IS_SHELL || typeof globalThis.URL === "function", "This browser/engine doesn't support URL API. Please use a modern version."); mono_assert(typeof globalThis.BigInt64Array === "function", "This browser/engine doesn't support BigInt64Array API. Please use a modern version."); if (MonoWasmThreads) { diff --git a/src/mono/wasm/runtime/web-socket.ts b/src/mono/wasm/runtime/web-socket.ts index 236c6037458c7..72ee49fd4d550 100644 --- a/src/mono/wasm/runtime/web-socket.ts +++ b/src/mono/wasm/runtime/web-socket.ts @@ -26,16 +26,20 @@ let mono_wasm_web_socket_close_warning = false; const ws_send_buffer_blocking_threshold = 65536; const emptyBuffer = new Uint8Array(); -export function ws_wasm_create(uri: string, sub_protocols: string[] | null, receive_status_ptr: VoidPtr, onClosed: (code: number, reason: string) => void): WebSocketExtension { +function verifyEnvironment() { if (ENVIRONMENT_IS_SHELL) { throw new Error("WebSockets are not supported in shell JS engine."); } if (typeof globalThis.WebSocket !== "function") { const message = ENVIRONMENT_IS_NODE - ? "Please install WebSocket package to enable networking support." + ? "Please install `ws` npm package to enable networking support." : "This browser doesn't support WebSocket API. Please use a modern browser."; throw new Error(message); } +} + +export function ws_wasm_create(uri: string, sub_protocols: string[] | null, receive_status_ptr: VoidPtr, onClosed: (code: number, reason: string) => void): WebSocketExtension { + verifyEnvironment(); mono_assert(uri && typeof uri === "string", () => `ERR12: Invalid uri ${typeof uri}`); const ws = new globalThis.WebSocket(uri, sub_protocols || undefined) as WebSocketExtension;