diff --git a/workloads/Dart/.gitignore b/workloads/Dart/.gitignore index 47b45a23..153fe610 100644 --- a/workloads/Dart/.gitignore +++ b/workloads/Dart/.gitignore @@ -1 +1,3 @@ -/wasm_gc_benchmarks/ +/flute/ +/dart-sdk/ +/dartsdk.zip diff --git a/workloads/Dart/README.md b/workloads/Dart/README.md index 6febcf07..767ad889 100644 --- a/workloads/Dart/README.md +++ b/workloads/Dart/README.md @@ -5,9 +5,8 @@ This is a WasmGC build of the `flute` benchmark, a Dart application using the It performs the Wasm/compute-only part of rendering a certain number of frames. Since JetStream runs in JS engine shells, the actual rendering to a Canvas is stubbed out. -The upstream repository containing pre-built WebAssembly binaries (which we -use here) is at https://github.com/mkustermann/wasm_gc_benchmarks. -The Dart source code of the flute application is at https://github.com/dart-lang/flute. +The benchmark harness (`addTaskQueue`) in `benchmark.js` comes from https://github.com/mkustermann/wasm_gc_benchmarks, +and `build.sh` compiles the WasmGC binaries directly from https://github.com/dart-lang/flute. ## Build Instructions diff --git a/workloads/Dart/build.log b/workloads/Dart/build.log index 2904e010..d60a0724 100644 --- a/workloads/Dart/build.log +++ b/workloads/Dart/build.log @@ -1,5 +1,16 @@ -Built on Thu Aug 14 04:47:37 PM CEST 2025 -Cloning into 'wasm_gc_benchmarks'... -13951e1 Roll new version of flute and regenerate flute-based benchmarks -Copying files from wasm_gc_benchmarks/ into build/ -Build success +Built on 2026-11-03T17:54:12Z +Downloading Dart SDK 3.14.0-315.3.beta (linux-x64)... +Toolchain versions +Dart SDK version: 3.14.0-315.3.beta (beta) (Thu Oct 22 14:42:43 2026 -0700) on "linux_x64" +Getting sources... +flute commit: +712764b Bump the github-actions group with 2 updates +Getting Dart dependencies... +Resolving dependencies... +Downloading packages... +Got dependencies! +Compiling flute.complex to wasm... +Generated wasm module '../../build/flute.complex.dart2wasm.wasm', and JS init file '../../build/flute.complex.dart2wasm.mjs'. +Compiling flute.todomvc to wasm... +Generated wasm module '../../build/flute.todomvc.dart2wasm.wasm', and JS init file '../../build/flute.todomvc.dart2wasm.mjs'. +Build success diff --git a/workloads/Dart/build.sh b/workloads/Dart/build.sh index 7d7efd73..9e3dcc7f 100755 --- a/workloads/Dart/build.sh +++ b/workloads/Dart/build.sh @@ -2,25 +2,63 @@ set -eo pipefail +# Dart 3.14 emits standard Wasm exception handling (`try_table`) by default +# (https://github.com/dart-lang/sdk/issues/54394). +# TODO: Switch DART_CHANNEL="stable" and DART_SDK_VERSION="3.14.0" once Dart +# 3.14 stable ships. +DART_CHANNEL="beta" +DART_SDK_VERSION="3.14.0-315.3.beta" +FLUTE_COMMIT="712764b186549d919a31d2fc8cc9da4e9a633eb3" + # Cleanup old files. -rm -rf wasm_gc_benchmarks/ +rm -rf flute/ dart-sdk/ dartsdk.zip rm -rf build/ BUILD_LOG="$(realpath build.log)" -echo -e "Built on $(date)" | tee "$BUILD_LOG" +echo -e "Built on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" | tee "$BUILD_LOG" + +case "$(uname -s)-$(uname -m)" in + Linux-x86_64) DART_PLATFORM="linux-x64" ;; + Linux-aarch64) DART_PLATFORM="linux-arm64" ;; + Darwin-x86_64) DART_PLATFORM="macos-x64" ;; + Darwin-arm64) DART_PLATFORM="macos-arm64" ;; + *) echo "Unsupported platform: $(uname -s)-$(uname -m)" >&2; exit 1 ;; +esac + +echo "Downloading Dart SDK $DART_SDK_VERSION ($DART_PLATFORM)..." | tee -a "$BUILD_LOG" +curl -sSf -o dartsdk.zip "https://storage.googleapis.com/dart-archive/channels/$DART_CHANNEL/release/$DART_SDK_VERSION/sdk/dartsdk-$DART_PLATFORM-release.zip" +unzip -q dartsdk.zip +rm dartsdk.zip +DART_BIN="$(realpath dart-sdk/bin/dart)" +"$DART_BIN" --disable-analytics >/dev/null 2>&1 || true -git clone https://github.com/mkustermann/wasm_gc_benchmarks 2>&1 | tee -a "$BUILD_LOG" -pushd wasm_gc_benchmarks/ +echo "Toolchain versions" | tee -a "$BUILD_LOG" +"$DART_BIN" --version 2>&1 | tee -a "$BUILD_LOG" + +echo "Getting sources..." | tee -a "$BUILD_LOG" +git clone -q https://github.com/dart-lang/flute.git flute 2>&1 | tee -a "$BUILD_LOG" +pushd flute/ +git checkout -q "$FLUTE_COMMIT" +echo "flute commit:" | tee -a "$BUILD_LOG" git log -1 --oneline | tee -a "$BUILD_LOG" + +pushd benchmarks/ +echo "Getting Dart dependencies..." | tee -a "$BUILD_LOG" +"$DART_BIN" pub get 2>&1 | tee -a "$BUILD_LOG" + +# Same flags as `wasm_gc_benchmarks/compile.sh`, which built the JetStream 3.0 +# binaries. -O2 is also what `flutter build web --wasm` uses for release builds, +# and --no-strip-wasm keeps the name section for profiling. +mkdir -p ../../build/ +echo "Compiling flute.complex to wasm..." | tee -a "$BUILD_LOG" +"$DART_BIN" compile wasm -Ddart.vm.product=true -O2 --no-strip-wasm -o ../../build/flute.complex.dart2wasm.wasm lib/complex.dart 2>&1 | tee -a "$BUILD_LOG" + +echo "Compiling flute.todomvc to wasm..." | tee -a "$BUILD_LOG" +"$DART_BIN" compile wasm -Ddart.vm.product=true -O2 --no-strip-wasm -o ../../build/flute.todomvc.dart2wasm.wasm lib/todomvc.dart 2>&1 | tee -a "$BUILD_LOG" +popd popd -echo "Copying files from wasm_gc_benchmarks/ into build/" | tee -a "$BUILD_LOG" -mkdir -p build/ | tee -a "$BUILD_LOG" -# Two Flute benchmark applications: complex and todomvc -cp wasm_gc_benchmarks/benchmarks-out/flute.complex.dart2wasm.{mjs,wasm} build/ | tee -a "$BUILD_LOG" -cp wasm_gc_benchmarks/benchmarks-out/flute.todomvc.dart2wasm.{mjs,wasm} build/ | tee -a "$BUILD_LOG" +# The benchmark only needs the .wasm and .mjs files. +rm -f build/*.support.js build/*.wasm.map echo "Build success" | tee -a "$BUILD_LOG" - -# TODO: We could actually build the application/benchmark from Dart sources with -# the dart2wasm compiler / Dart SDK. See `wasm_gc_benchmarks/compile.sh` diff --git a/workloads/Dart/build/flute.complex.dart2wasm.mjs b/workloads/Dart/build/flute.complex.dart2wasm.mjs index b3f5a771..7fa632dd 100644 --- a/workloads/Dart/build/flute.complex.dart2wasm.mjs +++ b/workloads/Dart/build/flute.complex.dart2wasm.mjs @@ -1,38 +1,34 @@ // Compiles a dart2wasm-generated main module from `source` which can then -// instantiatable via the `instantiate` method. +// be instantiated via the `instantiate` method. // // `source` needs to be a `Response` object (or promise thereof) e.g. created // via the `fetch()` JS API. export async function compileStreaming(source) { - const builtins = {builtins: ['js-string']}; + const builtins = {builtins: ['js-string'], importedStringConstants: ''}; return new CompiledApp( - await WebAssembly.compileStreaming(source, builtins), builtins); + await _compileStreaming(source, builtins), builtins); } -// Compiles a dart2wasm-generated wasm modules from `bytes` which is then -// instantiatable via the `instantiate` method. +// Compiles a dart2wasm-generated wasm module from `bytes` which is then +// instantiable via the `instantiate` method. export async function compile(bytes) { - const builtins = {builtins: ['js-string']}; + const builtins = {builtins: ['js-string'], importedStringConstants: ''}; return new CompiledApp(await WebAssembly.compile(bytes, builtins), builtins); } -// DEPRECATED: Please use `compile` or `compileStreaming` to get a compiled app, -// use `instantiate` method to get an instantiated app and then call -// `invokeMain` to invoke the main function. -export async function instantiate(modulePromise, importObjectPromise) { - var moduleOrCompiledApp = await modulePromise; - if (!(moduleOrCompiledApp instanceof CompiledApp)) { - moduleOrCompiledApp = new CompiledApp(moduleOrCompiledApp); +let _isCompileStreamingSupported; +async function _compileStreaming(source, builtins) { + _isCompileStreamingSupported ??= WebAssembly.compileStreaming( + new Response( + 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]), + {headers: {'Content-Type': 'application/wasm'}}, + ), + builtins, + ).then(() => false, (e) => e instanceof WebAssembly.CompileError); + if (await _isCompileStreamingSupported) { + return WebAssembly.compileStreaming(source, builtins); } - const instantiatedApp = await moduleOrCompiledApp.instantiate(await importObjectPromise); - return instantiatedApp.instantiatedModule; -} - -// DEPRECATED: Please use `compile` or `compileStreaming` to get a compiled app, -// use `instantiate` method to get an instantiated app and then call -// `invokeMain` to invoke the main function. -export const invoke = (moduleInstance, ...args) => { - moduleInstance.exports.$invokeMain(args); + return WebAssembly.compile(await (await source).arrayBuffer(), builtins); } class CompiledApp { @@ -42,18 +38,25 @@ class CompiledApp { } // The second argument is an options object containing: - // `loadDeferredWasm` is a JS function that takes a module name matching a - // wasm file produced by the dart2wasm compiler and returns the bytes to - // load the module. These bytes can be in either a format supported by - // `WebAssembly.compile` or `WebAssembly.compileStreaming`. - // `loadDynamicModule` is a JS function that takes two string names matching, - // in order, a wasm file produced by the dart2wasm compiler during dynamic - // module compilation and a corresponding js file produced by the same - // compilation. It should return a JS Array containing 2 elements. The first - // should be the bytes for the wasm module in a format supported by - // `WebAssembly.compile` or `WebAssembly.compileStreaming`. The second - // should be the result of using the JS 'import' API on the js file path. - async instantiate(additionalImports, {loadDeferredWasm, loadDynamicModule} = {}) { + // `loadDeferredModules` is a JS function that takes an array of module names + // matching wasm files produced by the dart2wasm compiler. It also takes a + // callback that should be invoked for each loaded module with 2 arguments: + // (1) the module name, (2) the loaded module in a format supported by + // `WebAssembly.compile` or `WebAssembly.compileStreaming`. The callback + // returns a Promise that resolves when the module is instantiated. + // loadDeferredModules should return a Promise that resolves when all the + // modules have been loaded and the callback promises have resolved. + // `loadDeferredId` is a JS function that takes load ID produced by the + // compiler when the `use-load-ids` option is passed. Each load ID maps to + // one or more wasm files as specified in the emitted JSON file. It also + // takes a callback that should be invoked for each loaded module with 2 + // arguments: (1) the module name, (2) the loaded module in a format + // supported by `WebAssembly.compile` or `WebAssembly.compileStreaming`. + // The callback returns a Promise that resolves when the module is + // instantiated. + // loadDeferredId should return a Promise that resolves when all the + // modules have been loaded and the callback promises have resolved. + async instantiate(additionalImports, {loadDeferredModules, loadDeferredId} = {}) { let dartInstance; // Prints to the console @@ -85,260 +88,163 @@ class CompiledApp { // Imports const dart2wasm = { - _6: (o,s,v) => o[s] = v, - _39: x0 => x0.length, - _41: (x0,x1) => x0[x1], - _69: () => Symbol("jsBoxedDartObjectProperty"), - _70: (decoder, codeUnits) => decoder.decode(codeUnits), - _71: () => new TextDecoder("utf-8", {fatal: true}), - _72: () => new TextDecoder("utf-8", {fatal: false}), - _73: (s) => +s, - _74: Date.now, - _76: s => new Date(s * 1000).getTimezoneOffset() * 60, - _77: s => { + AB: s => s.trimRight(), + B: s => printToConsole(s), + BB: (d, digits) => d.toFixed(digits), + C: Function.prototype.call.bind(Number.prototype.toString), + CB: (s) => +s, + D: Function.prototype.call.bind(BigInt.prototype.toString), + DB: s => { if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(s)) { return NaN; } return parseFloat(s); }, - _78: () => { - let stackString = new Error().stack.toString(); + E: (exn) => { + let stackString = exn.toString(); let frames = stackString.split('\n'); - let drop = 2; - if (frames[0] === 'Error') { + let drop = 4; + if (frames[0].startsWith('Error')) { drop += 1; } return frames.slice(drop).join('\n'); }, - _79: () => typeof dartUseDateNowForTicks !== "undefined", - _80: () => 1000 * performance.now(), - _81: () => Date.now(), - _84: () => new WeakMap(), - _85: (map, o) => map.get(o), - _86: (map, o, v) => map.set(o, v), - _99: s => JSON.stringify(s), - _100: s => printToConsole(s), - _101: (o, p, r) => o.replaceAll(p, () => r), - _103: Function.prototype.call.bind(String.prototype.toLowerCase), - _104: s => s.toUpperCase(), - _105: s => s.trim(), - _106: s => s.trimLeft(), - _107: s => s.trimRight(), - _108: (string, times) => string.repeat(times), - _109: Function.prototype.call.bind(String.prototype.indexOf), - _110: (s, p, i) => s.lastIndexOf(p, i), - _111: (string, token) => string.split(token), - _112: Object.is, - _113: o => o instanceof Array, - _118: a => a.pop(), - _119: (a, i) => a.splice(i, 1), - _120: (a, s) => a.join(s), - _121: (a, s, e) => a.slice(s, e), - _124: a => a.length, - _126: (a, i) => a[i], - _127: (a, i, v) => a[i] = v, - _130: (o, offsetInBytes, lengthInBytes) => { - var dst = new ArrayBuffer(lengthInBytes); - new Uint8Array(dst).set(new Uint8Array(o, offsetInBytes, lengthInBytes)); - return new DataView(dst); + EB: (x0,x1) => x0.matchMedia(x1), + F: () => new Error().stack, + FB: x0 => x0.matches, + G: s => JSON.stringify(s), + GB: () => globalThis.window, + H: Function.prototype.call.bind(Number.prototype.toString), + HB: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmF32ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } }, - _132: o => o instanceof Uint8Array, - _133: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length), - _135: (o, start, length) => new Int8Array(o.buffer, o.byteOffset + start, length), - _137: (o, start, length) => new Uint8ClampedArray(o.buffer, o.byteOffset + start, length), - _139: (o, start, length) => new Uint16Array(o.buffer, o.byteOffset + start, length), - _141: (o, start, length) => new Int16Array(o.buffer, o.byteOffset + start, length), - _143: (o, start, length) => new Uint32Array(o.buffer, o.byteOffset + start, length), - _145: (o, start, length) => new Int32Array(o.buffer, o.byteOffset + start, length), - _147: (o, start, length) => new BigInt64Array(o.buffer, o.byteOffset + start, length), - _149: (o, start, length) => new Float32Array(o.buffer, o.byteOffset + start, length), - _151: (o, start, length) => new Float64Array(o.buffer, o.byteOffset + start, length), - _152: (t, s) => t.set(s), - _154: (o) => new DataView(o.buffer, o.byteOffset, o.byteLength), - _156: o => o.buffer, - _157: o => o.byteOffset, - _158: Function.prototype.call.bind(Object.getOwnPropertyDescriptor(DataView.prototype, 'byteLength').get), - _159: (b, o) => new DataView(b, o), - _160: (b, o, l) => new DataView(b, o, l), - _161: Function.prototype.call.bind(DataView.prototype.getUint8), - _162: Function.prototype.call.bind(DataView.prototype.setUint8), - _163: Function.prototype.call.bind(DataView.prototype.getInt8), - _164: Function.prototype.call.bind(DataView.prototype.setInt8), - _165: Function.prototype.call.bind(DataView.prototype.getUint16), - _166: Function.prototype.call.bind(DataView.prototype.setUint16), - _167: Function.prototype.call.bind(DataView.prototype.getInt16), - _168: Function.prototype.call.bind(DataView.prototype.setInt16), - _169: Function.prototype.call.bind(DataView.prototype.getUint32), - _170: Function.prototype.call.bind(DataView.prototype.setUint32), - _171: Function.prototype.call.bind(DataView.prototype.getInt32), - _172: Function.prototype.call.bind(DataView.prototype.setInt32), - _175: Function.prototype.call.bind(DataView.prototype.getBigInt64), - _176: Function.prototype.call.bind(DataView.prototype.setBigInt64), - _177: Function.prototype.call.bind(DataView.prototype.getFloat32), - _178: Function.prototype.call.bind(DataView.prototype.setFloat32), - _179: Function.prototype.call.bind(DataView.prototype.getFloat64), - _180: Function.prototype.call.bind(DataView.prototype.setFloat64), - _193: (ms, c) => - setTimeout(() => dartInstance.exports.$invokeCallback(c),ms), - _194: (handle) => clearTimeout(handle), - _197: (c) => - queueMicrotask(() => dartInstance.exports.$invokeCallback(c)), - _232: (x0,x1) => x0.matchMedia(x1), - _233: (s, m) => { + I: Function.prototype.call.bind(String.prototype.indexOf), + IB: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmF64ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } + }, + J: (s, p, i) => s.lastIndexOf(p, i), + JB: (x0,x1) => x0.test(x1), + K: (exn) => { + if (exn instanceof Error) { + return exn.stack; + } else { + return null; + } + }, + KB: (handle) => clearTimeout(handle), + L: o => o === undefined, + LB: (map, o) => map.get(o), + M: s => s.trimLeft(), + MB: () => new WeakMap(), + N: (x0,x1) => x0.exec(x1), + NB: (map, o, v) => map.set(o, v), + O: x0 => x0.length, + OB: x0 => { globalThis.window.flutterCanvasKit = x0 }, + P: o => o, + PB: (o,s,v) => o[s] = v, + Q: o => { + if (o === undefined || o === null) return 0; + if (typeof o === 'number') return 1; + return 2; + }, + QB: () => Symbol("jsBoxedDartObjectProperty"), + R: (x0,x1) => { x0.lastIndex = x1 }, + RB: () => ({}), + S: o => o, + SB: s => new Date(s * 1000).getTimezoneOffset() * 60, + T: (s, m) => { try { return new RegExp(s, m); } catch (e) { return String(e); } }, - _234: (x0,x1) => x0.exec(x1), - _235: (x0,x1) => x0.test(x1), - _236: x0 => x0.pop(), - _238: o => o === undefined, - _240: o => typeof o === 'function' && o[jsWrappedDartFunctionSymbol] === true, - _243: o => o instanceof RegExp, - _244: (l, r) => l === r, - _245: o => o, - _246: o => o, - _247: o => o, - _249: o => o.length, - _251: (o, i) => o[i], - _252: f => f.dartFunction, - _253: () => ({}), - _263: o => String(o), - _265: o => { - if (o === undefined) return 1; - var type = typeof o; - if (type === 'boolean') return 2; - if (type === 'number') return 3; - if (type === 'string') return 4; - if (o instanceof Array) return 5; - if (ArrayBuffer.isView(o)) { - if (o instanceof Int8Array) return 6; - if (o instanceof Uint8Array) return 7; - if (o instanceof Uint8ClampedArray) return 8; - if (o instanceof Int16Array) return 9; - if (o instanceof Uint16Array) return 10; - if (o instanceof Int32Array) return 11; - if (o instanceof Uint32Array) return 12; - if (o instanceof Float32Array) return 13; - if (o instanceof Float64Array) return 14; - if (o instanceof DataView) return 15; - } - if (o instanceof ArrayBuffer) return 16; - // Feature check for `SharedArrayBuffer` before doing a type-check. - if (globalThis.SharedArrayBuffer !== undefined && - o instanceof SharedArrayBuffer) { - return 17; - } - return 18; + TB: Date.now, + U: o => o instanceof RegExp, + UB: (a, i) => a[i], + V: (string, times) => string.repeat(times), + VB: a => a.length, + W: o => o, + WB: o => o instanceof Array, + X: o => { + if (o === undefined || o === null) return 0; + if (typeof o === 'boolean') return 1; + return 2; }, - _271: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + XB: (a, s, e) => a.slice(s, e), + Y: x0 => x0.dotAll, + YB: (a, i, v) => a[i] = v, + Z: x0 => x0.unicode, + ZB: (a, i) => a.splice(i, 1), + a: x0 => x0.ignoreCase, + aB: a => a.pop(), + b: x0 => x0.multiline, + bB: (l, r) => l === r, + c: (string, token) => string.split(token), + d: x0 => x0.random(), + e: () => globalThis.Math, + f: (decoder, codeUnits) => decoder.decode(codeUnits), + g: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length), + h: () => new TextDecoder("utf-8", {fatal: true}), + i: () => new TextDecoder("utf-8", {fatal: false}), + j: s => s.toUpperCase(), + k: Object.is, + l: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { const setValue = dartInstance.exports.$wasmI8ArraySet; for (let i = 0; i < length; i++) { setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); } }, - _275: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + m: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { const setValue = dartInstance.exports.$wasmI32ArraySet; for (let i = 0; i < length; i++) { setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); } }, - _277: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { - const setValue = dartInstance.exports.$wasmF32ArraySet; - for (let i = 0; i < length; i++) { - setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); - } - }, - _279: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { - const setValue = dartInstance.exports.$wasmF64ArraySet; - for (let i = 0; i < length; i++) { - setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); - } - }, - _283: x0 => x0.index, - _285: x0 => x0.flags, - _286: x0 => x0.multiline, - _287: x0 => x0.ignoreCase, - _288: x0 => x0.unicode, - _289: x0 => x0.dotAll, - _290: (x0,x1) => { x0.lastIndex = x1 }, - _295: x0 => x0.random(), - _298: () => globalThis.Math, - _299: Function.prototype.call.bind(Number.prototype.toString), - _300: Function.prototype.call.bind(BigInt.prototype.toString), - _301: Function.prototype.call.bind(Number.prototype.toString), - _302: (d, digits) => d.toFixed(digits), - _2062: () => globalThis.window, - _8706: x0 => x0.matches, - _12689: x0 => { globalThis.window.flutterCanvasKit = x0 }, + n: Function.prototype.call.bind(String.prototype.toLowerCase), + o: (o, p, r) => o.replaceAll(p, () => r), + p: o => String(o), + q: (x0,x1) => x0[x1], + r: x0 => x0.index, + s: x0 => x0.flags, + t: s => s.trim(), + u: (c) => + queueMicrotask(() => dartInstance.exports.$invokeCallback(c)), + v: () => Date.now(), + w: (ms, c) => + setTimeout(() => dartInstance.exports.$invokeCallback(c),ms), + x: () => typeof dartUseDateNowForTicks !== "undefined", + y: () => 1000 * performance.now(), + z: x0 => x0.pop(), }; const baseImports = { - dart2wasm: dart2wasm, + _: dart2wasm, Math: Math, Date: Date, Object: Object, Array: Array, Reflect: Reflect, - S: new Proxy({}, { get(_, prop) { return prop; } }), - - }; - - const jsStringPolyfill = { - "charCodeAt": (s, i) => s.charCodeAt(i), - "compare": (s1, s2) => { - if (s1 < s2) return -1; - if (s1 > s2) return 1; - return 0; + WebAssembly: { + JSTag: WebAssembly.JSTag, }, - "concat": (s1, s2) => s1 + s2, - "equals": (s1, s2) => s1 === s2, - "fromCharCode": (i) => String.fromCharCode(i), - "length": (s) => s.length, - "substring": (s, a, b) => s.substring(a, b), - "fromCharCodeArray": (a, start, end) => { - if (end <= start) return ''; + "": new Proxy({}, { get(_, prop) { return prop; } }), - const read = dartInstance.exports.$wasmI16ArrayGet; - let result = ''; - let index = start; - const chunkLength = Math.min(end - index, 500); - let array = new Array(chunkLength); - while (index < end) { - const newChunkLength = Math.min(end - index, 500); - for (let i = 0; i < newChunkLength; i++) { - array[i] = read(a, index++); - } - if (newChunkLength < chunkLength) { - array = array.slice(0, newChunkLength); - } - result += String.fromCharCode(...array); - } - return result; - }, - "intoCharCodeArray": (s, a, start) => { - if (s === '') return 0; - - const write = dartInstance.exports.$wasmI16ArraySet; - for (var i = 0; i < s.length; ++i) { - write(a, start++, s.charCodeAt(i)); - } - return s.length; - }, - "test": (s) => typeof s == "string", }; - dartInstance = await WebAssembly.instantiate(this.module, { ...baseImports, ...additionalImports, - "wasm:js-string": jsStringPolyfill, }); return new InstantiatedApp(this, dartInstance); diff --git a/workloads/Dart/build/flute.complex.dart2wasm.wasm b/workloads/Dart/build/flute.complex.dart2wasm.wasm index 5b40365e..d1e47551 100644 Binary files a/workloads/Dart/build/flute.complex.dart2wasm.wasm and b/workloads/Dart/build/flute.complex.dart2wasm.wasm differ diff --git a/workloads/Dart/build/flute.todomvc.dart2wasm.mjs b/workloads/Dart/build/flute.todomvc.dart2wasm.mjs index 662f198d..c4beda1e 100644 --- a/workloads/Dart/build/flute.todomvc.dart2wasm.mjs +++ b/workloads/Dart/build/flute.todomvc.dart2wasm.mjs @@ -1,38 +1,34 @@ // Compiles a dart2wasm-generated main module from `source` which can then -// instantiatable via the `instantiate` method. +// be instantiated via the `instantiate` method. // // `source` needs to be a `Response` object (or promise thereof) e.g. created // via the `fetch()` JS API. export async function compileStreaming(source) { - const builtins = {builtins: ['js-string']}; + const builtins = {builtins: ['js-string'], importedStringConstants: ''}; return new CompiledApp( - await WebAssembly.compileStreaming(source, builtins), builtins); + await _compileStreaming(source, builtins), builtins); } -// Compiles a dart2wasm-generated wasm modules from `bytes` which is then -// instantiatable via the `instantiate` method. +// Compiles a dart2wasm-generated wasm module from `bytes` which is then +// instantiable via the `instantiate` method. export async function compile(bytes) { - const builtins = {builtins: ['js-string']}; + const builtins = {builtins: ['js-string'], importedStringConstants: ''}; return new CompiledApp(await WebAssembly.compile(bytes, builtins), builtins); } -// DEPRECATED: Please use `compile` or `compileStreaming` to get a compiled app, -// use `instantiate` method to get an instantiated app and then call -// `invokeMain` to invoke the main function. -export async function instantiate(modulePromise, importObjectPromise) { - var moduleOrCompiledApp = await modulePromise; - if (!(moduleOrCompiledApp instanceof CompiledApp)) { - moduleOrCompiledApp = new CompiledApp(moduleOrCompiledApp); +let _isCompileStreamingSupported; +async function _compileStreaming(source, builtins) { + _isCompileStreamingSupported ??= WebAssembly.compileStreaming( + new Response( + 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]), + {headers: {'Content-Type': 'application/wasm'}}, + ), + builtins, + ).then(() => false, (e) => e instanceof WebAssembly.CompileError); + if (await _isCompileStreamingSupported) { + return WebAssembly.compileStreaming(source, builtins); } - const instantiatedApp = await moduleOrCompiledApp.instantiate(await importObjectPromise); - return instantiatedApp.instantiatedModule; -} - -// DEPRECATED: Please use `compile` or `compileStreaming` to get a compiled app, -// use `instantiate` method to get an instantiated app and then call -// `invokeMain` to invoke the main function. -export const invoke = (moduleInstance, ...args) => { - moduleInstance.exports.$invokeMain(args); + return WebAssembly.compile(await (await source).arrayBuffer(), builtins); } class CompiledApp { @@ -42,18 +38,25 @@ class CompiledApp { } // The second argument is an options object containing: - // `loadDeferredWasm` is a JS function that takes a module name matching a - // wasm file produced by the dart2wasm compiler and returns the bytes to - // load the module. These bytes can be in either a format supported by - // `WebAssembly.compile` or `WebAssembly.compileStreaming`. - // `loadDynamicModule` is a JS function that takes two string names matching, - // in order, a wasm file produced by the dart2wasm compiler during dynamic - // module compilation and a corresponding js file produced by the same - // compilation. It should return a JS Array containing 2 elements. The first - // should be the bytes for the wasm module in a format supported by - // `WebAssembly.compile` or `WebAssembly.compileStreaming`. The second - // should be the result of using the JS 'import' API on the js file path. - async instantiate(additionalImports, {loadDeferredWasm, loadDynamicModule} = {}) { + // `loadDeferredModules` is a JS function that takes an array of module names + // matching wasm files produced by the dart2wasm compiler. It also takes a + // callback that should be invoked for each loaded module with 2 arguments: + // (1) the module name, (2) the loaded module in a format supported by + // `WebAssembly.compile` or `WebAssembly.compileStreaming`. The callback + // returns a Promise that resolves when the module is instantiated. + // loadDeferredModules should return a Promise that resolves when all the + // modules have been loaded and the callback promises have resolved. + // `loadDeferredId` is a JS function that takes load ID produced by the + // compiler when the `use-load-ids` option is passed. Each load ID maps to + // one or more wasm files as specified in the emitted JSON file. It also + // takes a callback that should be invoked for each loaded module with 2 + // arguments: (1) the module name, (2) the loaded module in a format + // supported by `WebAssembly.compile` or `WebAssembly.compileStreaming`. + // The callback returns a Promise that resolves when the module is + // instantiated. + // loadDeferredId should return a Promise that resolves when all the + // modules have been loaded and the callback promises have resolved. + async instantiate(additionalImports, {loadDeferredModules, loadDeferredId} = {}) { let dartInstance; // Prints to the console @@ -85,264 +88,167 @@ class CompiledApp { // Imports const dart2wasm = { - _6: (o,s,v) => o[s] = v, - _39: x0 => x0.length, - _41: (x0,x1) => x0[x1], - _69: () => Symbol("jsBoxedDartObjectProperty"), - _70: (decoder, codeUnits) => decoder.decode(codeUnits), - _71: () => new TextDecoder("utf-8", {fatal: true}), - _72: () => new TextDecoder("utf-8", {fatal: false}), - _73: (s) => +s, - _74: Date.now, - _76: s => new Date(s * 1000).getTimezoneOffset() * 60, - _77: s => { + AB: s => s.trimRight(), + B: s => printToConsole(s), + BB: (d, digits) => d.toFixed(digits), + C: Function.prototype.call.bind(Number.prototype.toString), + CB: (s) => +s, + D: Function.prototype.call.bind(BigInt.prototype.toString), + DB: s => { if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(s)) { return NaN; } return parseFloat(s); }, - _78: () => { - let stackString = new Error().stack.toString(); + E: (exn) => { + let stackString = exn.toString(); let frames = stackString.split('\n'); - let drop = 2; - if (frames[0] === 'Error') { + let drop = 4; + if (frames[0].startsWith('Error')) { drop += 1; } return frames.slice(drop).join('\n'); }, - _79: () => typeof dartUseDateNowForTicks !== "undefined", - _80: () => 1000 * performance.now(), - _81: () => Date.now(), - _84: () => new WeakMap(), - _85: (map, o) => map.get(o), - _86: (map, o, v) => map.set(o, v), - _99: s => JSON.stringify(s), - _100: s => printToConsole(s), - _101: (o, p, r) => o.replaceAll(p, () => r), - _103: Function.prototype.call.bind(String.prototype.toLowerCase), - _104: s => s.toUpperCase(), - _105: s => s.trim(), - _106: s => s.trimLeft(), - _107: s => s.trimRight(), - _108: (string, times) => string.repeat(times), - _109: Function.prototype.call.bind(String.prototype.indexOf), - _110: (s, p, i) => s.lastIndexOf(p, i), - _111: (string, token) => string.split(token), - _112: Object.is, - _113: o => o instanceof Array, - _118: a => a.pop(), - _119: (a, i) => a.splice(i, 1), - _120: (a, s) => a.join(s), - _121: (a, s, e) => a.slice(s, e), - _124: a => a.length, - _126: (a, i) => a[i], - _127: (a, i, v) => a[i] = v, - _130: (o, offsetInBytes, lengthInBytes) => { - var dst = new ArrayBuffer(lengthInBytes); - new Uint8Array(dst).set(new Uint8Array(o, offsetInBytes, lengthInBytes)); - return new DataView(dst); + EB: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmF32ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } }, - _132: o => o instanceof Uint8Array, - _133: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length), - _135: (o, start, length) => new Int8Array(o.buffer, o.byteOffset + start, length), - _137: (o, start, length) => new Uint8ClampedArray(o.buffer, o.byteOffset + start, length), - _139: (o, start, length) => new Uint16Array(o.buffer, o.byteOffset + start, length), - _141: (o, start, length) => new Int16Array(o.buffer, o.byteOffset + start, length), - _143: (o, start, length) => new Uint32Array(o.buffer, o.byteOffset + start, length), - _145: (o, start, length) => new Int32Array(o.buffer, o.byteOffset + start, length), - _147: (o, start, length) => new BigInt64Array(o.buffer, o.byteOffset + start, length), - _149: (o, start, length) => new Float32Array(o.buffer, o.byteOffset + start, length), - _151: (o, start, length) => new Float64Array(o.buffer, o.byteOffset + start, length), - _152: (t, s) => t.set(s), - _154: (o) => new DataView(o.buffer, o.byteOffset, o.byteLength), - _156: o => o.buffer, - _157: o => o.byteOffset, - _158: Function.prototype.call.bind(Object.getOwnPropertyDescriptor(DataView.prototype, 'byteLength').get), - _159: (b, o) => new DataView(b, o), - _160: (b, o, l) => new DataView(b, o, l), - _161: Function.prototype.call.bind(DataView.prototype.getUint8), - _162: Function.prototype.call.bind(DataView.prototype.setUint8), - _163: Function.prototype.call.bind(DataView.prototype.getInt8), - _164: Function.prototype.call.bind(DataView.prototype.setInt8), - _165: Function.prototype.call.bind(DataView.prototype.getUint16), - _166: Function.prototype.call.bind(DataView.prototype.setUint16), - _167: Function.prototype.call.bind(DataView.prototype.getInt16), - _168: Function.prototype.call.bind(DataView.prototype.setInt16), - _169: Function.prototype.call.bind(DataView.prototype.getUint32), - _170: Function.prototype.call.bind(DataView.prototype.setUint32), - _171: Function.prototype.call.bind(DataView.prototype.getInt32), - _172: Function.prototype.call.bind(DataView.prototype.setInt32), - _175: Function.prototype.call.bind(DataView.prototype.getBigInt64), - _176: Function.prototype.call.bind(DataView.prototype.setBigInt64), - _177: Function.prototype.call.bind(DataView.prototype.getFloat32), - _178: Function.prototype.call.bind(DataView.prototype.setFloat32), - _179: Function.prototype.call.bind(DataView.prototype.getFloat64), - _180: Function.prototype.call.bind(DataView.prototype.setFloat64), - _193: (ms, c) => - setTimeout(() => dartInstance.exports.$invokeCallback(c),ms), - _194: (handle) => clearTimeout(handle), - _195: (ms, c) => + F: () => new Error().stack, + FB: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + const setValue = dartInstance.exports.$wasmF64ArraySet; + for (let i = 0; i < length; i++) { + setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); + } + }, + G: s => JSON.stringify(s), + GB: (handle) => clearInterval(handle), + H: Function.prototype.call.bind(Number.prototype.toString), + HB: (ms, c) => setInterval(() => dartInstance.exports.$invokeCallback(c), ms), - _196: (handle) => clearInterval(handle), - _197: (c) => - queueMicrotask(() => dartInstance.exports.$invokeCallback(c)), - _198: () => Date.now(), - _232: (x0,x1) => x0.matchMedia(x1), - _233: (s, m) => { + I: Function.prototype.call.bind(String.prototype.indexOf), + IB: () => Date.now(), + J: (s, p, i) => s.lastIndexOf(p, i), + JB: (handle) => clearTimeout(handle), + K: (exn) => { + if (exn instanceof Error) { + return exn.stack; + } else { + return null; + } + }, + KB: (x0,x1) => x0.matchMedia(x1), + L: o => o === undefined, + LB: x0 => x0.matches, + M: s => s.trimLeft(), + MB: () => globalThis.window, + N: (x0,x1) => x0.exec(x1), + NB: (map, o, v) => map.set(o, v), + O: x0 => x0.length, + OB: (map, o) => map.get(o), + P: o => o, + PB: () => new WeakMap(), + Q: o => { + if (o === undefined || o === null) return 0; + if (typeof o === 'number') return 1; + return 2; + }, + QB: (x0,x1) => x0.test(x1), + R: (x0,x1) => { x0.lastIndex = x1 }, + RB: x0 => { globalThis.window.flutterCanvasKit = x0 }, + S: o => o, + SB: (o,s,v) => o[s] = v, + T: (s, m) => { try { return new RegExp(s, m); } catch (e) { return String(e); } }, - _234: (x0,x1) => x0.exec(x1), - _235: (x0,x1) => x0.test(x1), - _236: x0 => x0.pop(), - _238: o => o === undefined, - _240: o => typeof o === 'function' && o[jsWrappedDartFunctionSymbol] === true, - _243: o => o instanceof RegExp, - _244: (l, r) => l === r, - _245: o => o, - _246: o => o, - _247: o => o, - _249: o => o.length, - _251: (o, i) => o[i], - _252: f => f.dartFunction, - _253: () => ({}), - _263: o => String(o), - _265: o => { - if (o === undefined) return 1; - var type = typeof o; - if (type === 'boolean') return 2; - if (type === 'number') return 3; - if (type === 'string') return 4; - if (o instanceof Array) return 5; - if (ArrayBuffer.isView(o)) { - if (o instanceof Int8Array) return 6; - if (o instanceof Uint8Array) return 7; - if (o instanceof Uint8ClampedArray) return 8; - if (o instanceof Int16Array) return 9; - if (o instanceof Uint16Array) return 10; - if (o instanceof Int32Array) return 11; - if (o instanceof Uint32Array) return 12; - if (o instanceof Float32Array) return 13; - if (o instanceof Float64Array) return 14; - if (o instanceof DataView) return 15; - } - if (o instanceof ArrayBuffer) return 16; - // Feature check for `SharedArrayBuffer` before doing a type-check. - if (globalThis.SharedArrayBuffer !== undefined && - o instanceof SharedArrayBuffer) { - return 17; - } - return 18; + TB: () => Symbol("jsBoxedDartObjectProperty"), + U: o => o instanceof RegExp, + UB: () => ({}), + V: (string, times) => string.repeat(times), + VB: s => new Date(s * 1000).getTimezoneOffset() * 60, + W: o => o, + WB: Date.now, + X: o => { + if (o === undefined || o === null) return 0; + if (typeof o === 'boolean') return 1; + return 2; }, - _271: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + XB: (a, i) => a[i], + Y: x0 => x0.dotAll, + YB: a => a.length, + Z: x0 => x0.unicode, + ZB: o => o instanceof Array, + a: x0 => x0.ignoreCase, + aB: (a, s, e) => a.slice(s, e), + b: x0 => x0.multiline, + bB: (a, i) => a.splice(i, 1), + c: (string, token) => string.split(token), + cB: a => a.pop(), + d: x0 => x0.random(), + dB: (a, i, v) => a[i] = v, + e: () => globalThis.Math, + eB: (l, r) => l === r, + f: (decoder, codeUnits) => decoder.decode(codeUnits), + g: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length), + h: () => new TextDecoder("utf-8", {fatal: true}), + i: () => new TextDecoder("utf-8", {fatal: false}), + j: s => s.toUpperCase(), + k: Object.is, + l: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { const setValue = dartInstance.exports.$wasmI8ArraySet; for (let i = 0; i < length; i++) { setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); } }, - _275: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { + m: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { const setValue = dartInstance.exports.$wasmI32ArraySet; for (let i = 0; i < length; i++) { setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); } }, - _277: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { - const setValue = dartInstance.exports.$wasmF32ArraySet; - for (let i = 0; i < length; i++) { - setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); - } - }, - _279: (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => { - const setValue = dartInstance.exports.$wasmF64ArraySet; - for (let i = 0; i < length; i++) { - setValue(wasmArray, wasmArrayOffset + i, jsArray[jsArrayOffset + i]); - } - }, - _283: x0 => x0.index, - _285: x0 => x0.flags, - _286: x0 => x0.multiline, - _287: x0 => x0.ignoreCase, - _288: x0 => x0.unicode, - _289: x0 => x0.dotAll, - _290: (x0,x1) => { x0.lastIndex = x1 }, - _295: x0 => x0.random(), - _298: () => globalThis.Math, - _299: Function.prototype.call.bind(Number.prototype.toString), - _300: Function.prototype.call.bind(BigInt.prototype.toString), - _301: Function.prototype.call.bind(Number.prototype.toString), - _302: (d, digits) => d.toFixed(digits), - _2062: () => globalThis.window, - _8706: x0 => x0.matches, - _12689: x0 => { globalThis.window.flutterCanvasKit = x0 }, + n: Function.prototype.call.bind(String.prototype.toLowerCase), + o: (o, p, r) => o.replaceAll(p, () => r), + p: o => String(o), + q: (x0,x1) => x0[x1], + r: x0 => x0.index, + s: x0 => x0.flags, + t: s => s.trim(), + u: (c) => + queueMicrotask(() => dartInstance.exports.$invokeCallback(c)), + v: () => Date.now(), + w: (ms, c) => + setTimeout(() => dartInstance.exports.$invokeCallback(c),ms), + x: () => typeof dartUseDateNowForTicks !== "undefined", + y: () => 1000 * performance.now(), + z: x0 => x0.pop(), }; const baseImports = { - dart2wasm: dart2wasm, + _: dart2wasm, Math: Math, Date: Date, Object: Object, Array: Array, Reflect: Reflect, - S: new Proxy({}, { get(_, prop) { return prop; } }), - - }; - - const jsStringPolyfill = { - "charCodeAt": (s, i) => s.charCodeAt(i), - "compare": (s1, s2) => { - if (s1 < s2) return -1; - if (s1 > s2) return 1; - return 0; + WebAssembly: { + JSTag: WebAssembly.JSTag, }, - "concat": (s1, s2) => s1 + s2, - "equals": (s1, s2) => s1 === s2, - "fromCharCode": (i) => String.fromCharCode(i), - "length": (s) => s.length, - "substring": (s, a, b) => s.substring(a, b), - "fromCharCodeArray": (a, start, end) => { - if (end <= start) return ''; + "": new Proxy({}, { get(_, prop) { return prop; } }), - const read = dartInstance.exports.$wasmI16ArrayGet; - let result = ''; - let index = start; - const chunkLength = Math.min(end - index, 500); - let array = new Array(chunkLength); - while (index < end) { - const newChunkLength = Math.min(end - index, 500); - for (let i = 0; i < newChunkLength; i++) { - array[i] = read(a, index++); - } - if (newChunkLength < chunkLength) { - array = array.slice(0, newChunkLength); - } - result += String.fromCharCode(...array); - } - return result; - }, - "intoCharCodeArray": (s, a, start) => { - if (s === '') return 0; - - const write = dartInstance.exports.$wasmI16ArraySet; - for (var i = 0; i < s.length; ++i) { - write(a, start++, s.charCodeAt(i)); - } - return s.length; - }, - "test": (s) => typeof s == "string", }; - dartInstance = await WebAssembly.instantiate(this.module, { ...baseImports, ...additionalImports, - "wasm:js-string": jsStringPolyfill, }); return new InstantiatedApp(this, dartInstance); diff --git a/workloads/Dart/build/flute.todomvc.dart2wasm.wasm b/workloads/Dart/build/flute.todomvc.dart2wasm.wasm index 0577db62..1c8036b9 100644 Binary files a/workloads/Dart/build/flute.todomvc.dart2wasm.wasm and b/workloads/Dart/build/flute.todomvc.dart2wasm.wasm differ