[browser] detect and assert engine features (#88846)
authorPavel Savara <pavel.savara@gmail.com>
Fri, 14 Jul 2023 13:03:32 +0000 (15:03 +0200)
committerGitHub <noreply@github.com>
Fri, 14 Jul 2023 13:03:32 +0000 (15:03 +0200)
src/mono/wasm/runtime/http.ts
src/mono/wasm/runtime/loader/index.ts
src/mono/wasm/runtime/loader/polyfills.ts
src/mono/wasm/runtime/loader/worker.ts
src/mono/wasm/runtime/web-socket.ts

index 9034494..ff9dbbb 100644 (file)
@@ -2,15 +2,26 @@
 // 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";
 
+
+function verifyEnvironment() {
+    if (typeof globalThis.fetch !== "function" || typeof globalThis.AbortController !== "function") {
+        const message = ENVIRONMENT_IS_NODE
+            ? "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();
 }
 
@@ -38,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<ResponseExtension> {
+    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");
index 9c2f0d0..6818186 100644 (file)
@@ -3,6 +3,7 @@
 
 import type { DotnetHostBuilder } from "../types";
 import { mono_exit } from "./exit";
+import { verifyEnvironment } 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;
 
+verifyEnvironment();
+
 export { dotnet, exit };
 export default legacyEntrypoint;
index eed7a45..cf4b5ab 100644 (file)
@@ -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";
@@ -14,6 +18,19 @@ const URLPolyfill = class URL {
     }
 };
 
+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) {
+        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<void> {
 
     const scriptUrlQuery =/* webpackIgnore: true */import.meta.url;
index 2f20241..a22c65f 100644 (file)
@@ -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";
index 00ca6c8..72ee49f 100644 (file)
@@ -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";
@@ -26,7 +26,20 @@ let mono_wasm_web_socket_close_warning = false;
 const ws_send_buffer_blocking_threshold = 65536;
 const emptyBuffer = new Uint8Array();
 
+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 `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;