From 3b750c5545f880506418453542a49e08a77dcbae Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 10 Feb 2025 06:17:49 -0800 Subject: [PATCH] [dart2wasm] Generate `*.support.js` feature detection files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file will contain a JS expression that evaluates to a boolean. If it (at runtime) evalutes to * `true` it means that all required features are supported by the JS environment and the dart2wasm-compiled app can be used * `false` it means some features were not present in the JS environment and the dart2wasm-compiled app shouldn't be used, instead a dart2js fallback may be used We introduce this mechanism to allow users, at compilation time, to tell dart2wasm to take advantage of new spec features and allow the runtime to self-detect whether they are available and fallback to dart2js if not. The first feature we introduce (already in this PR) is `--require-js-string-builtin` that will tell dart2wasm it can assume the `js-string` builtin is available (and emit corresponding `*.support.js` code to detect it). If the flag was passed, we take advantage of the `js-string` import mechanism for string constants that doesn't require emitting them in the mjs file (which significantly reduces code size and improves startup time - compared with emitting JS strings in the mjs file). We enable `--require-js-string-builtin` on one CI configuration for testing that if we don't use any polyfill, the imports of the builtin functions as well as magical utf8-encoded wasm imports work. We also use a template mechanism to generate `*.mjs` as the code becomes more readable (e.g. to conditionally include the js string polyfill) Issue https://github.com/dart-lang/sdk/issues/59951 Change-Id: Ic7e7818a2d5269095935022941352beeb9fed731 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/408781 Reviewed-by: Ömer Ağacan Commit-Queue: Martin Kustermann --- build/rbe/rewrapper_dart.py | 2 +- pkg/dart2wasm/lib/compile.dart | 55 ++++++- pkg/dart2wasm/lib/compiler_options.dart | 1 - pkg/dart2wasm/lib/dart2wasm.dart | 5 +- pkg/dart2wasm/lib/generate_wasm.dart | 6 +- pkg/dart2wasm/lib/js/runtime_blob.dart | 155 +++++++++++++------- pkg/dart2wasm/lib/js/runtime_generator.dart | 28 ++-- pkg/dart2wasm/lib/translator.dart | 37 ++++- tools/bots/test_matrix.json | 3 +- 9 files changed, 210 insertions(+), 82 deletions(-) diff --git a/build/rbe/rewrapper_dart.py b/build/rbe/rewrapper_dart.py index 76b753dd7cb..40e4f4952cb 100644 --- a/build/rbe/rewrapper_dart.py +++ b/build/rbe/rewrapper_dart.py @@ -82,7 +82,7 @@ def list_imports(uri, exec_root, package_config): ]: continue # Imports must happen before definitions. - if tokens[0] in ['const', 'class', 'enum']: + if tokens[0] in ['const', 'class', 'enum', 'final']: break if 2 <= len(tokens ) and tokens[0] == 'if' and tokens[1] == '(dart.library.io)': diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart index 61dd17addc6..b3dfa7f9a0f 100644 --- a/pkg/dart2wasm/lib/compile.dart +++ b/pkg/dart2wasm/lib/compile.dart @@ -48,8 +48,9 @@ sealed class CompilationResult {} class CompilationSuccess extends CompilationResult { final Map wasmModules; final String jsRuntime; + final String supportJs; - CompilationSuccess(this.wasmModules, this.jsRuntime); + CompilationSuccess(this.wasmModules, this.jsRuntime, this.supportJs); } class CompilationError extends CompilationResult {} @@ -238,11 +239,8 @@ Future compileToModule( String? depFile = options.depFile; if (depFile != null) { - writeDepfile( - compilerOptions.fileSystem, - component.uriToSource.keys, - options.outputFile, - depFile); + writeDepfile(compilerOptions.fileSystem, component.uriToSource.keys, + options.outputFile, depFile); } final generateSourceMaps = options.translatorOptions.generateSourceMaps; @@ -262,7 +260,50 @@ Future compileToModule( String jsRuntime = jsRuntimeFinalizer.generate( translator.functions.translatedProcedures, translator.internalizedStringsForJSRuntime, + translator.options.requireJsStringBuiltin, mode); - return CompilationSuccess(wasmModules, jsRuntime); + final supportJs = _generateSupportJs(options.translatorOptions); + return CompilationSuccess(wasmModules, jsRuntime, supportJs); +} + +String _generateSupportJs(TranslatorOptions options) { + // Copied from + // https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/gc/index.js + // + // Uses WasmGC types and will only validate correctly if the engine supports + // WasmGC: + // ``` + // (module + // (type $type0 (struct (field $field0 i8))) + // ) + // ``` + // + // NOTE: Once we support more feature detections we may use + // `package:wasm_builder` to create the module instead of having a fixed one + // here. + const String supportsWasmGC = + 'WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,95,1,120,0]))'; + + // Imports a `js-string` builtin spec function *with wrong signature*. An engine + // + // * *without* knowledge about `js-string` builtin would accept such an import at + // validation time. + // + // * *with* knowledge about `js-string` would refuse it as the signature + // used to import the `cast` function is not according to `js-string` spec + // + // ``` + // (module + // (func $wasm:js-string.cast (;0;) (import "wasm:js-string" "cast")) + // ) + // ``` + const String supportsJsStringBuiltins = + '!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,2,23,1,14,119,97,115,109,58,106,115,45,115,116,114,105,110,103,4,99,97,115,116,0,0]),{"builtins":["js-string"]})'; + + final requiredFeatures = [ + supportsWasmGC, + if (options.requireJsStringBuiltin) supportsJsStringBuiltins + ]; + return '(${requiredFeatures.join('&&')})'; } diff --git a/pkg/dart2wasm/lib/compiler_options.dart b/pkg/dart2wasm/lib/compiler_options.dart index fa526289737..da74fdc84d8 100644 --- a/pkg/dart2wasm/lib/compiler_options.dart +++ b/pkg/dart2wasm/lib/compiler_options.dart @@ -15,7 +15,6 @@ class WasmCompilerOptions { Uri mainUri; String outputFile; String? depFile; - String? outputJSRuntimeFile; Uri? dynamicModuleMainUri; Uri? dynamicInterfaceUri; Uri? dynamicModuleMetadataFile; diff --git a/pkg/dart2wasm/lib/dart2wasm.dart b/pkg/dart2wasm/lib/dart2wasm.dart index 352b3a9d3fe..9cb8c5a3f61 100644 --- a/pkg/dart2wasm/lib/dart2wasm.dart +++ b/pkg/dart2wasm/lib/dart2wasm.dart @@ -78,8 +78,6 @@ final List