Merge main
- Merge upstream main branch - Partially merge https://github.com/WebKit/JetStream/pull/167/ to display sub-times Change-Id: I9f30772ff2d6d068b09e43df3cb57da35b231ca2 Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/WebKit/JetStream/+/6911418 Reviewed-by: Victor Gomes <victorgomes@chromium.org>
This commit is contained in:
parent
11ac3bea19
commit
97f348ec9d
250 changed files with 755371 additions and 533941 deletions
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
|
|
@ -16,6 +16,7 @@ jobs:
|
|||
env:
|
||||
GITHUB_ACTIONS_OUTPUT: ""
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
browser: [chrome, firefox, jsc, safari, spidermonkey, v8]
|
||||
steps:
|
||||
|
|
|
|||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,7 +1,10 @@
|
|||
.DS_Store
|
||||
.directory
|
||||
/node_modules
|
||||
node_modules
|
||||
|
||||
# Ignore auto-generated files by VS & VSCode.
|
||||
/.vs/
|
||||
/.vscode/
|
||||
|
||||
# v8.log is generated by the d8-shell if profiling is enabled
|
||||
v8.log
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ class Benchmark {
|
|||
romBinary;
|
||||
|
||||
async init() {
|
||||
Module.wasmBinary = await getBinary(wasmBinary);
|
||||
this.romBinary = await getBinary(romBinary);
|
||||
Module.wasmBinary = await JetStream.getBinary(JetStream.preload.wasmBinary);
|
||||
this.romBinary = await JetStream.getBinary(JetStream.preload.romBinary);
|
||||
}
|
||||
|
||||
async runIteration() {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ popd
|
|||
|
||||
echo "Copying files from 8bitbench/ into build/" | tee -a "$BUILD_LOG"
|
||||
mkdir -p build/{lib/fast-text-encoding-1.0.3/,rust/pkg/,assets/}
|
||||
cp 8bitbench/lib/fast-text-encoding-1.0.3/text.js build/lib/fast-text-encoding-1.0.3/
|
||||
cp 8bitbench/rust/pkg/{emu_bench.js,emu_bench_bg.wasm} build/rust/pkg
|
||||
cp 8bitbench/assets/tutorial/full_game.bin build/assets/program.bin
|
||||
|
||||
|
|
|
|||
|
|
@ -168,12 +168,3 @@ function bubbleSort(array, lessThan)
|
|||
end--;
|
||||
}
|
||||
}
|
||||
|
||||
let currentTime;
|
||||
if (this.performance && performance.now)
|
||||
currentTime = function() { return performance.now() };
|
||||
else if (this.preciseTime)
|
||||
currentTime = function() { return preciseTime() * 1000; };
|
||||
else
|
||||
currentTime = function() { return +new Date(); };
|
||||
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ class Benchmark {
|
|||
let sources = [];
|
||||
|
||||
const files = [
|
||||
[airBlob, {}]
|
||||
, [basicBlob, {}]
|
||||
, [inspectorBlob, {}]
|
||||
, [babylonBlob, {sourceType: "module"}]
|
||||
[JetStream.preload.airBlob, {}],
|
||||
[JetStream.preload.basicBlob, {}],
|
||||
[JetStream.preload.inspectorBlob, {}],
|
||||
[JetStream.preload.babylonBlob, {sourceType: "module"}],
|
||||
];
|
||||
|
||||
for (let [file, options] of files)
|
||||
sources.push([file, await getString(file), options]);
|
||||
sources.push([file, await JetStream.getString(file), options]);
|
||||
|
||||
this.sources = sources;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
let currentTime;
|
||||
if (this.performance && performance.now)
|
||||
currentTime = function() { return performance.now() };
|
||||
else if (this.preciseTime)
|
||||
currentTime = function() { return preciseTime() * 1000; };
|
||||
else
|
||||
currentTime = function() { return +new Date(); };
|
||||
|
||||
|
|
@ -2,8 +2,11 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Excerpt from `build/run_wasm.js` to add own task queue implementation, since
|
||||
// `setTimeout` and `queueMicrotask` are not always available in shells.
|
||||
// Excerpt from `wasm_gc_benchmarks/tools/run_wasm.js` to add own task queue
|
||||
// implementation, since `setTimeout` and `queueMicrotask` are not always
|
||||
// available in shells.
|
||||
// TODO: Now (2025-08-14) that all shells have `setTimeout` available, can we
|
||||
// remove this? Talk to Dart2wasm folks.
|
||||
function addTaskQueue(self) {
|
||||
"use strict";
|
||||
|
||||
|
|
@ -62,8 +65,7 @@ function addTaskQueue(self) {
|
|||
ms = Math.max(0, ms);
|
||||
var id = timerIdCounter++;
|
||||
// A callback can be scheduled at most once.
|
||||
// (console.assert is only available on D8)
|
||||
// if (isD8) console.assert(f.$timerId === undefined);
|
||||
console.assert(f.$timerId === undefined);
|
||||
f.$timerId = id;
|
||||
timerIds[id] = f;
|
||||
if (ms == 0 && !isNextTimerDue()) {
|
||||
|
|
@ -262,8 +264,8 @@ class Benchmark {
|
|||
// The generated JavaScript code from dart2wasm is an ES module, which we
|
||||
// can only load with a dynamic import (since this file is not a module.)
|
||||
|
||||
Module.wasmBinary = await getBinary(wasmBinary);
|
||||
this.dart2wasmJsModule = await dynamicImport(jsModule);
|
||||
Module.wasmBinary = await JetStream.getBinary(JetStream.preload.wasmBinary);
|
||||
this.dart2wasmJsModule = await JetStream.dynamicImport(JetStream.preload.jsModule);
|
||||
}
|
||||
|
||||
async runIteration() {
|
||||
|
|
@ -283,7 +285,7 @@ class Benchmark {
|
|||
const framesToDraw = 100;
|
||||
const initialFramesToSkip = 0;
|
||||
const dartArgs = [
|
||||
startTimeSinceEpochSeconds,
|
||||
startTimeSinceEpochSeconds.toString(),
|
||||
framesToDraw.toString(),
|
||||
initialFramesToSkip.toString()
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Built on 2025-03-06 09:52:17+01:00
|
||||
Built on Thu Aug 14 04:47:37 PM CEST 2025
|
||||
Cloning into 'wasm_gc_benchmarks'...
|
||||
f80892d Update Dart SDK, switch wasm to -O2, recompile performance benchmarks
|
||||
13951e1 Roll new version of flute and regenerate flute-based benchmarks
|
||||
Copying files from wasm_gc_benchmarks/ into build/
|
||||
Build success
|
||||
|
|
|
|||
|
|
@ -7,19 +7,18 @@ rm -rf wasm_gc_benchmarks/
|
|||
rm -rf build/
|
||||
|
||||
BUILD_LOG="$(realpath build.log)"
|
||||
echo -e "Built on $(date --rfc-3339=seconds)" | tee "$BUILD_LOG"
|
||||
echo -e "Built on $(date)" | tee "$BUILD_LOG"
|
||||
|
||||
git clone https://github.com/mkustermann/wasm_gc_benchmarks |& tee -a "$BUILD_LOG"
|
||||
git clone https://github.com/mkustermann/wasm_gc_benchmarks 2>&1 | tee -a "$BUILD_LOG"
|
||||
pushd wasm_gc_benchmarks/
|
||||
git log -1 --oneline | tee -a "$BUILD_LOG"
|
||||
popd
|
||||
|
||||
echo "Copying files from wasm_gc_benchmarks/ into build/" | tee -a "$BUILD_LOG"
|
||||
mkdir -p build/ | tee -a "$BUILD_LOG"
|
||||
# Generic Dart2wasm runner.
|
||||
cp wasm_gc_benchmarks/tools/run_wasm.js build/ | tee -a "$BUILD_LOG"
|
||||
# "Flute Complex" benchmark application.
|
||||
cp wasm_gc_benchmarks/benchmarks-out/flute.dart2wasm.{mjs,wasm} 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"
|
||||
|
||||
echo "Build success" | tee -a "$BUILD_LOG"
|
||||
|
||||
|
|
|
|||
358
Dart/build/flute.complex.dart2wasm.mjs
Normal file
358
Dart/build/flute.complex.dart2wasm.mjs
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
// Compiles a dart2wasm-generated main module from `source` which can then
|
||||
// instantiatable 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']};
|
||||
return new CompiledApp(
|
||||
await WebAssembly.compileStreaming(source, builtins), builtins);
|
||||
}
|
||||
|
||||
// Compiles a dart2wasm-generated wasm modules from `bytes` which is then
|
||||
// instantiatable via the `instantiate` method.
|
||||
export async function compile(bytes) {
|
||||
const builtins = {builtins: ['js-string']};
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
class CompiledApp {
|
||||
constructor(module, builtins) {
|
||||
this.module = module;
|
||||
this.builtins = builtins;
|
||||
}
|
||||
|
||||
// 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} = {}) {
|
||||
let dartInstance;
|
||||
|
||||
// Prints to the console
|
||||
function printToConsole(value) {
|
||||
if (typeof dartPrint == "function") {
|
||||
dartPrint(value);
|
||||
return;
|
||||
}
|
||||
if (typeof console == "object" && typeof console.log != "undefined") {
|
||||
console.log(value);
|
||||
return;
|
||||
}
|
||||
if (typeof print == "function") {
|
||||
print(value);
|
||||
return;
|
||||
}
|
||||
|
||||
throw "Unable to print message: " + value;
|
||||
}
|
||||
|
||||
// A special symbol attached to functions that wrap Dart functions.
|
||||
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
|
||||
|
||||
function finalizeWrapper(dartFunction, wrapped) {
|
||||
wrapped.dartFunction = dartFunction;
|
||||
wrapped[jsWrappedDartFunctionSymbol] = true;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
// 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 => {
|
||||
if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(s)) {
|
||||
return NaN;
|
||||
}
|
||||
return parseFloat(s);
|
||||
},
|
||||
_78: () => {
|
||||
let stackString = new Error().stack.toString();
|
||||
let frames = stackString.split('\n');
|
||||
let drop = 2;
|
||||
if (frames[0] === '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);
|
||||
},
|
||||
_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) => {
|
||||
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;
|
||||
},
|
||||
_271: (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) => {
|
||||
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 },
|
||||
|
||||
};
|
||||
|
||||
const baseImports = {
|
||||
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;
|
||||
},
|
||||
"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 '';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
class InstantiatedApp {
|
||||
constructor(compiledApp, instantiatedModule) {
|
||||
this.compiledApp = compiledApp;
|
||||
this.instantiatedModule = instantiatedModule;
|
||||
}
|
||||
|
||||
// Call the main function with the given arguments.
|
||||
invokeMain(...args) {
|
||||
this.instantiatedModule.exports.$invokeMain(args);
|
||||
}
|
||||
}
|
||||
BIN
Dart/build/flute.complex.dart2wasm.wasm
Normal file
BIN
Dart/build/flute.complex.dart2wasm.wasm
Normal file
Binary file not shown.
362
Dart/build/flute.todomvc.dart2wasm.mjs
Normal file
362
Dart/build/flute.todomvc.dart2wasm.mjs
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
// Compiles a dart2wasm-generated main module from `source` which can then
|
||||
// instantiatable 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']};
|
||||
return new CompiledApp(
|
||||
await WebAssembly.compileStreaming(source, builtins), builtins);
|
||||
}
|
||||
|
||||
// Compiles a dart2wasm-generated wasm modules from `bytes` which is then
|
||||
// instantiatable via the `instantiate` method.
|
||||
export async function compile(bytes) {
|
||||
const builtins = {builtins: ['js-string']};
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
class CompiledApp {
|
||||
constructor(module, builtins) {
|
||||
this.module = module;
|
||||
this.builtins = builtins;
|
||||
}
|
||||
|
||||
// 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} = {}) {
|
||||
let dartInstance;
|
||||
|
||||
// Prints to the console
|
||||
function printToConsole(value) {
|
||||
if (typeof dartPrint == "function") {
|
||||
dartPrint(value);
|
||||
return;
|
||||
}
|
||||
if (typeof console == "object" && typeof console.log != "undefined") {
|
||||
console.log(value);
|
||||
return;
|
||||
}
|
||||
if (typeof print == "function") {
|
||||
print(value);
|
||||
return;
|
||||
}
|
||||
|
||||
throw "Unable to print message: " + value;
|
||||
}
|
||||
|
||||
// A special symbol attached to functions that wrap Dart functions.
|
||||
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
|
||||
|
||||
function finalizeWrapper(dartFunction, wrapped) {
|
||||
wrapped.dartFunction = dartFunction;
|
||||
wrapped[jsWrappedDartFunctionSymbol] = true;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
// 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 => {
|
||||
if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(s)) {
|
||||
return NaN;
|
||||
}
|
||||
return parseFloat(s);
|
||||
},
|
||||
_78: () => {
|
||||
let stackString = new Error().stack.toString();
|
||||
let frames = stackString.split('\n');
|
||||
let drop = 2;
|
||||
if (frames[0] === '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);
|
||||
},
|
||||
_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) =>
|
||||
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) => {
|
||||
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;
|
||||
},
|
||||
_271: (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) => {
|
||||
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 },
|
||||
|
||||
};
|
||||
|
||||
const baseImports = {
|
||||
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;
|
||||
},
|
||||
"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 '';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
class InstantiatedApp {
|
||||
constructor(compiledApp, instantiatedModule) {
|
||||
this.compiledApp = compiledApp;
|
||||
this.instantiatedModule = instantiatedModule;
|
||||
}
|
||||
|
||||
// Call the main function with the given arguments.
|
||||
invokeMain(...args) {
|
||||
this.instantiatedModule.exports.$invokeMain(args);
|
||||
}
|
||||
}
|
||||
BIN
Dart/build/flute.todomvc.dart2wasm.wasm
Normal file
BIN
Dart/build/flute.todomvc.dart2wasm.wasm
Normal file
Binary file not shown.
278
JetStream.css
278
JetStream.css
|
|
@ -23,13 +23,38 @@
|
|||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
html {
|
||||
:root {
|
||||
--color-primary: #34AADC;
|
||||
--color-secondary: #86D9FF;
|
||||
--text-color-inverse: white;
|
||||
--text-color-primary: black;
|
||||
--text-color-secondary: #555555;
|
||||
--text-color-tertiary: #444444;
|
||||
--text-color-subtle: #6c6c71;
|
||||
--text-color-very-subtle: #8E8E93;
|
||||
--heading-color: #2C98D1;
|
||||
--link-hover-color: #0086BF;
|
||||
--button-color-primary: rgb(52, 170, 220);
|
||||
--error-text-color: #d24a59;
|
||||
--benchmark-heading-color: rgb(183, 183, 183);
|
||||
--benchmark-error-text-color: #ff8686;
|
||||
--benchmark-done-result-color: #4A4A4A;
|
||||
--gap: 3rem;
|
||||
--width: 200px;
|
||||
}
|
||||
|
||||
html,
|
||||
svg text {
|
||||
font-family: "Helvetica Neue", Helvetica, Verdana, sans-serif;
|
||||
font-size: 62.5%;
|
||||
font-synthesis: none;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap);
|
||||
margin: 0;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 400;
|
||||
|
|
@ -39,32 +64,48 @@ body {
|
|||
background-size: 100vw;
|
||||
padding-bottom: 0px;
|
||||
background-image: url('clouds.svg');
|
||||
overflow-y: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.overflow-scroll {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.overflow-visible {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
|
||||
::selection {
|
||||
background-color: #34AADC;
|
||||
color: white;
|
||||
background-color: var(--color-primary);
|
||||
color: var(--text-color-inverse);
|
||||
}
|
||||
|
||||
main {
|
||||
display: block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap);
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--gap);
|
||||
max-width: 1180px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
main p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
-webkit-user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
perspective: 600;
|
||||
}
|
||||
|
|
@ -108,51 +149,59 @@ img {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.summary + .summary {
|
||||
padding-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.summary:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
article, .summary {
|
||||
article,
|
||||
.summary {
|
||||
max-width: 70rem;
|
||||
margin: 0 auto 1rem;
|
||||
margin: 0 auto 0 auto;
|
||||
opacity: 0;
|
||||
animation: fadein 0.5s ease-in-out forwards;
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
|
||||
article {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap);
|
||||
}
|
||||
h1 {
|
||||
color: black;
|
||||
color: var(--text-color-primary);
|
||||
text-align: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
h2, h3, h4, h5, h6 {
|
||||
color: #2C98D1;
|
||||
text-align: left;
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: var(--heading-color);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
h4, h5, h6 {
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: left;
|
||||
color: #555555;
|
||||
margin: 0 0 3rem 0;
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
|
||||
h5, h6 {
|
||||
h5,
|
||||
h6 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
color: #444444;
|
||||
color: var(--text-color-tertiary);
|
||||
}
|
||||
|
||||
dt {
|
||||
|
|
@ -169,16 +218,12 @@ dd {
|
|||
|
||||
a:link,
|
||||
a:visited {
|
||||
color: #34AADC;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:active {
|
||||
color: #0086BF;
|
||||
}
|
||||
|
||||
#status {
|
||||
margin: 2rem 0rem;
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
#status label,
|
||||
|
|
@ -187,16 +232,15 @@ a.button {
|
|||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
font-size: 2rem;
|
||||
background-color: rgb(52,170,220);
|
||||
background-image: linear-gradient(180deg, rgba(134,217,255,1) -80%, rgba(52,170,220,1) 100%);
|
||||
color: rgb(255,255,255);
|
||||
border: 1px solid rgb(52,170,220);
|
||||
background-color: var(--button-color-primary);
|
||||
background-image: linear-gradient(180deg, rgba(134, 217, 255, 1) -80%, rgb(52, 170, 220) 100%);
|
||||
color: var(--text-color-inverse);
|
||||
border: 1px solid var(--button-color-primary);
|
||||
border-radius: 2px;
|
||||
padding: 0.3rem 9rem 0.5rem;
|
||||
padding: 0.3rem 0.3rem 0.5rem;
|
||||
text-align: center;
|
||||
width: var(--width);
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
|
@ -224,12 +268,8 @@ a.button {
|
|||
color: transparent;
|
||||
background-image: linear-gradient(132deg, #96E5FF 0%, #96E5FF 2%, #86D9FF 42%, #8BDAFF 84%, #96E5FF 98%, #96E5FF 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-size: 1200px 100%;
|
||||
background-repeat: no-repeat;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
|
@ -238,8 +278,9 @@ a.button {
|
|||
margin: 0 auto 1rem;
|
||||
}
|
||||
|
||||
.error h2, .error p {
|
||||
color: #d24a59;
|
||||
.error h2,
|
||||
.error p {
|
||||
color: var(--error-text-color);
|
||||
margin-bottom: 0;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
|
|
@ -260,17 +301,22 @@ a.button {
|
|||
}
|
||||
|
||||
#result-summary label {
|
||||
color: #6c6c71;
|
||||
color: var(--text-color-subtle);
|
||||
}
|
||||
|
||||
#result-summary .score {
|
||||
font-weight: bold;
|
||||
font-size: 4rem;
|
||||
line-height: 1;
|
||||
color: #34AADC;
|
||||
color: var(--color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.benchmark .plot svg {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#result-summary .score .interval {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
|
|
@ -280,57 +326,80 @@ a.button {
|
|||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
gap: var(--gap);
|
||||
margin: 0 calc(var(--gap) * -1) 0 calc(-var(--gap) * -1);
|
||||
animation: fadein 500ms ease-out forwards;
|
||||
opacity: 0;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
padding-bottom: var(--gap);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.benchmark {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
max-width: 20%;
|
||||
min-width: 200px;
|
||||
min-width: var(--width);
|
||||
text-align: left;
|
||||
color: #8E8E93;
|
||||
color: var(--text-color-very-subtle);
|
||||
font-size: 1.6rem;
|
||||
margin: 0 1.6rem 3rem 0;
|
||||
|
||||
scroll-margin-top: 20vh;
|
||||
scroll-margin-bottom: 20vh;
|
||||
}
|
||||
|
||||
.benchmark h4, .benchmark .result, .benchmark label {
|
||||
.benchmark .result.detail {
|
||||
display: none;
|
||||
}
|
||||
.details .benchmark .result.detail {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.benchmark h4,
|
||||
.benchmark .result,
|
||||
.benchmark label,
|
||||
.benchmark .plot {
|
||||
color: transparent;
|
||||
background: linear-gradient(160deg, rgba(249,249,249,1) 0%, rgba(238,238,238,1) 100%);
|
||||
background: linear-gradient(160deg, rgba(249, 249, 249, 1) 0%, rgba(238, 238, 238, 1) 100%);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.benchmark h3 {
|
||||
color: rgb(183, 183, 183);
|
||||
color: var(--benchmark-heading-color);
|
||||
}
|
||||
|
||||
.benchmark-running h4, .benchmark-running .result, .benchmark-running label {
|
||||
color: #86D9FF;
|
||||
background-color: #86D9FF;
|
||||
.benchmark-running h4,
|
||||
.benchmark-running .result,
|
||||
.benchmark-running label,
|
||||
.benchmark-running .plot {
|
||||
color: var(--color-secondary);
|
||||
background-color: var(--color-secondary);
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
|
||||
.benchmark-done h3, .benchmark-done h4, .benchmark-done .result, .benchmark-done label {
|
||||
.benchmark-done h3,
|
||||
.benchmark-done h4,
|
||||
.benchmark-done .result,
|
||||
.benchmark-done label,
|
||||
.benchmark-done .plot {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
-webkit-touch-callout: revert;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.benchmark-error h4, .benchmark-error .result, .benchmark-error label {
|
||||
color: #ff8686;
|
||||
background-color: #ff8686;
|
||||
.benchmark-error h4,
|
||||
.benchmark-error .result,
|
||||
.benchmark-error label,
|
||||
.benchmark-error .plot {
|
||||
color: var(--benchmark-error-text-color);
|
||||
background-color: var(--benchmark-error-text-color);
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.benchmark-error h3 {
|
||||
color: #ff8686;
|
||||
color: var(--benchmark-error-text-color);
|
||||
}
|
||||
|
||||
.benchmark h3 {
|
||||
|
|
@ -342,11 +411,37 @@ a.button {
|
|||
.benchmark-running h3 {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
color: #34AADC;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.benchmark-done h3 {
|
||||
color: #6c6c71;
|
||||
color: var(--text-color-subtle);
|
||||
}
|
||||
|
||||
.benchmark a.info {
|
||||
display: inline-block;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 100%;
|
||||
background-color: var(--benchmark-heading-color);
|
||||
color: var(--text-color-inverse) !important;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
font-family: 'Times New Roman', Times, serif;
|
||||
line-height: 1.6rem;
|
||||
margin-top: -0.2rem;
|
||||
|
||||
}
|
||||
|
||||
.benchmark-running a.info {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.benchmark-done a.info {
|
||||
background-color: var(--text-color-subtle);
|
||||
}
|
||||
|
||||
.benchmark h3 a,
|
||||
|
|
@ -359,7 +454,7 @@ a.button {
|
|||
}
|
||||
|
||||
.benchmark-done h3 a:hover {
|
||||
color: #34AADC;
|
||||
color: var(--color-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
|
@ -371,7 +466,7 @@ a.button {
|
|||
}
|
||||
|
||||
.benchmark-done h4 {
|
||||
color: #34AADC;
|
||||
color: var(--color-primary);
|
||||
background-color: none;
|
||||
}
|
||||
|
||||
|
|
@ -388,8 +483,15 @@ a.button {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
#result-summary .benchmark {
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
margin-left: 1.6rem;
|
||||
}
|
||||
|
||||
.benchmark-done .result {
|
||||
color: #4A4A4A;
|
||||
color: var(--benchmark-done-result-color);
|
||||
}
|
||||
|
||||
.benchmark label {
|
||||
|
|
@ -397,14 +499,27 @@ a.button {
|
|||
}
|
||||
|
||||
.benchmark-done label {
|
||||
color: #8E8E93;
|
||||
color: var(--text-color-very-subtle);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
dt:target {
|
||||
background-color: var(--color-secondary);
|
||||
color: var(--text-color-inverse);
|
||||
padding-left: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.plot svg circle {
|
||||
fill: var(--color-primary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
@ -412,10 +527,11 @@ a.button {
|
|||
|
||||
@keyframes scaledown {
|
||||
from {
|
||||
transform: scale(1.3,1.3);
|
||||
transform: scale(1.3, 1.3);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1,1);
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,6 +539,7 @@ a.button {
|
|||
0% {
|
||||
background-position: -1200px center;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 100vw center;
|
||||
}
|
||||
|
|
@ -433,6 +550,7 @@ a.button {
|
|||
opacity: 0;
|
||||
transform: rotateY(-85deg) translateZ(200px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: rotateY(0deg) translateZ(0px);
|
||||
|
|
@ -451,14 +569,4 @@ a.button {
|
|||
#jetstreams {
|
||||
background-size: 200%;
|
||||
}
|
||||
|
||||
|
||||
article, .summary {
|
||||
padding-top: 10rem;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
a.button {
|
||||
padding: 0.3rem 6rem 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.9 KiB |
2280
JetStreamDriver.js
2280
JetStreamDriver.js
File diff suppressed because it is too large
Load diff
1
Kotlin-compose/.gitignore
vendored
Normal file
1
Kotlin-compose/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/compose-multiplatform/
|
||||
18
Kotlin-compose/README.md
Normal file
18
Kotlin-compose/README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Kotlin/Wasm Compose Multiplatform Benchmark
|
||||
|
||||
Citing https://github.com/JetBrains/compose-multiplatform:
|
||||
|
||||
> [Compose Multiplatform](https://jb.gg/cmp) is a declarative framework for sharing UIs across multiple platforms with Kotlin.
|
||||
[...]
|
||||
Compose for Web is based on [Kotlin/Wasm](https://kotl.in/wasm), the newest target for Kotlin Multiplatform projects.
|
||||
It allows Kotlin developers to run their code in the browser with all the benefits that WebAssembly has to offer, such as good and predictable performance for your applications.
|
||||
|
||||
## Build Instructions
|
||||
|
||||
See or run `build.sh`.
|
||||
See `build.log` for the last build time, used sources, and toolchain versions.
|
||||
|
||||
## Running in JS shells
|
||||
|
||||
To run the unmodified upstream benchmark, without the JetStream driver, see the
|
||||
upstream repo, specifically https://github.com/JetBrains/compose-multiplatform/blob/master/benchmarks/multiplatform/README.md
|
||||
158
Kotlin-compose/benchmark.js
Normal file
158
Kotlin-compose/benchmark.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2025 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Excerpt from `polyfills.mjs` from the upstream Kotlin compose-multiplatform
|
||||
// benchmark directory, with minor changes for JetStream.
|
||||
|
||||
globalThis.window ??= globalThis;
|
||||
|
||||
globalThis.navigator ??= {};
|
||||
if (!globalThis.navigator.languages) {
|
||||
globalThis.navigator.languages = ['en-US', 'en'];
|
||||
globalThis.navigator.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
||||
globalThis.navigator.platform = "MacIntel";
|
||||
}
|
||||
|
||||
// Compose reads `window.isSecureContext` in its Clipboard feature:
|
||||
globalThis.isSecureContext = false;
|
||||
|
||||
// Disable explicit GC (it wouldn't work in browsers anyway).
|
||||
globalThis.gc = () => {
|
||||
// DEBUG
|
||||
// console.log("gc()");
|
||||
}
|
||||
|
||||
class URL {
|
||||
href;
|
||||
constructor(url, base) {
|
||||
// DEBUG
|
||||
// console.log('URL', url, base);
|
||||
this.href = url;
|
||||
}
|
||||
}
|
||||
globalThis.URL = URL;
|
||||
|
||||
// We always polyfill `fetch` and `instantiateStreaming` for consistency between
|
||||
// engine shells and browsers and to avoid introducing network latency into the
|
||||
// first iteration / instantiation measurement.
|
||||
// The downside is that this doesn't test streaming Wasm instantiation, which we
|
||||
// are willing to accept.
|
||||
let preload = { /* Initialized in init() below due to async. */ };
|
||||
const originalFetch = globalThis.fetch ?? function(url) {
|
||||
throw new Error("no fetch available");
|
||||
}
|
||||
globalThis.fetch = async function(url) {
|
||||
// DEBUG
|
||||
// console.log('fetch', url);
|
||||
|
||||
// Redirect some paths to cached/preloaded resources.
|
||||
if (preload[url]) {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
arrayBuffer() { return preload[url]; },
|
||||
async blob() {
|
||||
return {
|
||||
size: preload[url].byteLength,
|
||||
async arrayBuffer() { return preload[url]; }
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// This should only be called in the browser, where fetch() is available.
|
||||
return originalFetch(url);
|
||||
};
|
||||
globalThis.WebAssembly.instantiateStreaming = async function(m,i) {
|
||||
// DEBUG
|
||||
// console.log('instantiateStreaming',m,i);
|
||||
return WebAssembly.instantiate((await m).arrayBuffer(),i);
|
||||
};
|
||||
|
||||
// Provide `setTimeout` for Kotlin coroutines.
|
||||
const originalSetTimeout = setTimeout;
|
||||
globalThis.setTimeout = function(f, delayMs) {
|
||||
// DEBUG
|
||||
// console.log('setTimeout', f, t);
|
||||
|
||||
// Deep in the Compose UI framework, one task is scheduled every 16ms, see
|
||||
// https://github.com/JetBrains/compose-multiplatform-core/blob/a52f2981b9bc7cdba1d1fbe71654c4be448ebea7/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/spatial/RectManager.kt#L138
|
||||
// and
|
||||
// https://github.com/JetBrains/compose-multiplatform-core/blob/a52f2981b9bc7cdba1d1fbe71654c4be448ebea7/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnLayoutRectChangedModifier.kt#L56
|
||||
// We don't want to delay work in the Wall-time based measurement in JetStream,
|
||||
// but executing this immediately (without delay) produces redundant work that
|
||||
// is not realistic for a full-browser Kotlin/multiplatform application either,
|
||||
// according to Kotlin/JetBrains folks.
|
||||
// Hence the early return for 16ms delays.
|
||||
if (delayMs === 16) return;
|
||||
if (delayMs !== 0) {
|
||||
throw new Error('Unexpected delay for setTimeout polyfill: ' + delayMs);
|
||||
}
|
||||
originalSetTimeout(f);
|
||||
}
|
||||
|
||||
// Don't automatically run the main function on instantiation.
|
||||
globalThis.skipFunMain = true;
|
||||
|
||||
// Prevent this from being detected as a shell environment, so that we use the
|
||||
// same code paths as in the browser.
|
||||
// See `compose-benchmarks-benchmarks.uninstantiated.mjs`.
|
||||
delete globalThis.d8;
|
||||
delete globalThis.inIon;
|
||||
delete globalThis.jscOptions;
|
||||
|
||||
class Benchmark {
|
||||
skikoInstantiate;
|
||||
mainInstantiate;
|
||||
wasmInstanceExports;
|
||||
|
||||
async init() {
|
||||
// DEBUG
|
||||
// console.log("init");
|
||||
|
||||
preload = {
|
||||
'skiko.wasm': await JetStream.getBinary(JetStream.preload.skikoWasmBinary),
|
||||
'./compose-benchmarks-benchmarks.wasm': await JetStream.getBinary(JetStream.preload.composeWasmBinary),
|
||||
'./composeResources/compose_benchmarks.benchmarks.generated.resources/drawable/compose-multiplatform.png': await JetStream.getBinary(JetStream.preload.inputImageCompose),
|
||||
'./composeResources/compose_benchmarks.benchmarks.generated.resources/drawable/example1_cat.jpg': await JetStream.getBinary(JetStream.preload.inputImageCat),
|
||||
'./composeResources/compose_benchmarks.benchmarks.generated.resources/files/example1_compose-community-primary.png': await JetStream.getBinary(JetStream.preload.inputImageComposeCommunity),
|
||||
'./composeResources/compose_benchmarks.benchmarks.generated.resources/font/jetbrainsmono_italic.ttf': await JetStream.getBinary(JetStream.preload.inputFontItalic),
|
||||
'./composeResources/compose_benchmarks.benchmarks.generated.resources/font/jetbrainsmono_regular.ttf': await JetStream.getBinary(JetStream.preload.inputFontRegular),
|
||||
};
|
||||
|
||||
// We patched `skiko.mjs` to not immediately instantiate the `skiko.wasm`
|
||||
// module, so that we can move the dynamic JS import here but measure
|
||||
// WebAssembly compilation and instantiation as part of the first iteration.
|
||||
this.skikoInstantiate = (await JetStream.dynamicImport(JetStream.preload.skikoJsModule)).default;
|
||||
this.mainInstantiate = (await JetStream.dynamicImport(JetStream.preload.composeJsModule)).instantiate;
|
||||
}
|
||||
|
||||
async runIteration() {
|
||||
// DEBUG
|
||||
// console.log("runIteration");
|
||||
|
||||
// Compile once in the first iteration.
|
||||
if (!this.wasmInstanceExports) {
|
||||
const skikoExports = (await this.skikoInstantiate()).wasmExports;
|
||||
this.wasmInstanceExports = (await this.mainInstantiate({ './skiko.mjs': skikoExports })).exports;
|
||||
}
|
||||
|
||||
// We render/animate/process fewer frames than in the upstream benchmark,
|
||||
// since we run multiple iterations in JetStream (to measure first, worst,
|
||||
// and average runtime) and don't want the overall workload to take too long.
|
||||
const frameCountFactor = 5;
|
||||
|
||||
// The factors for the subitems are chosen to make them take the same order
|
||||
// of magnitude in terms of Wall time.
|
||||
await this.wasmInstanceExports.customLaunch("AnimatedVisibility", 100 * frameCountFactor);
|
||||
await this.wasmInstanceExports.customLaunch("LazyGrid", 1 * frameCountFactor);
|
||||
await this.wasmInstanceExports.customLaunch("LazyGrid-ItemLaunchedEffect", 1 * frameCountFactor);
|
||||
// The `SmoothScroll` variants of the LazyGrid workload are much faster.
|
||||
await this.wasmInstanceExports.customLaunch("LazyGrid-SmoothScroll", 5 * frameCountFactor);
|
||||
await this.wasmInstanceExports.customLaunch("LazyGrid-SmoothScroll-ItemLaunchedEffect", 5 * frameCountFactor);
|
||||
// This is quite GC-heavy, is this realistic for Kotlin/compose applications?
|
||||
await this.wasmInstanceExports.customLaunch("VisualEffects", 1 * frameCountFactor);
|
||||
await this.wasmInstanceExports.customLaunch("MultipleComponents-NoVectorGraphics", 10 * frameCountFactor);
|
||||
}
|
||||
}
|
||||
5
Kotlin-compose/build.log
Normal file
5
Kotlin-compose/build.log
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
Built on 2025-08-14 12:27:42+02:00
|
||||
Cloning into 'compose-multiplatform'...
|
||||
84dad4d3f6 Use custom skiko (0.9.4.3) to fix the FinalizationRegistry API usage for web targets
|
||||
Copying generated files into build/
|
||||
Build success
|
||||
46
Kotlin-compose/build.sh
Executable file
46
Kotlin-compose/build.sh
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# Cleanup old files.
|
||||
rm -rf build/
|
||||
rm -rf compose-multiplatform/
|
||||
|
||||
BUILD_LOG="$(realpath build.log)"
|
||||
echo -e "Built on $(date --rfc-3339=seconds)" | tee "$BUILD_LOG"
|
||||
|
||||
# Build the benchmark from source.
|
||||
# FIXME: Use main branch and remove hotfix patch below, once
|
||||
# https://youtrack.jetbrains.com/issue/SKIKO-1040 is resolved upstream.
|
||||
# See https://github.com/WebKit/JetStream/pull/84#discussion_r2252418900.
|
||||
git clone -b ok/jetstream3_hotfix https://github.com/JetBrains/compose-multiplatform.git |& tee -a "$BUILD_LOG"
|
||||
pushd compose-multiplatform/
|
||||
git log -1 --oneline | tee -a "$BUILD_LOG"
|
||||
# Do not read `isD8` in the main function which decides whether to run eagerly.
|
||||
# Instead, just patch that out statically.
|
||||
# TODO: Upstream that fix to the compose-multiplatform repo.
|
||||
git apply ../empty-main-function.patch | tee -a "$BUILD_LOG"
|
||||
# FIXME: Use stable 2.3 Kotlin/Wasm toolchain, once available around Sep 2025.
|
||||
git apply ../use-beta-toolchain.patch | tee -a "$BUILD_LOG"
|
||||
pushd benchmarks/multiplatform
|
||||
./gradlew :benchmarks:wasmJsProductionExecutableCompileSync
|
||||
# For building polyfills and JavaScript launcher to run in d8 (which inspires the benchmark.js launcher here):
|
||||
# ./gradlew :benchmarks:buildD8Distribution
|
||||
BUILD_SRC_DIR="compose-multiplatform/benchmarks/multiplatform/build/wasm/packages/compose-benchmarks-benchmarks/kotlin"
|
||||
popd
|
||||
popd
|
||||
|
||||
echo "Copying generated files into build/" | tee -a "$BUILD_LOG"
|
||||
mkdir -p build/ | tee -a "$BUILD_LOG"
|
||||
cp $BUILD_SRC_DIR/compose-benchmarks-benchmarks.{wasm,uninstantiated.mjs} build/ | tee -a "$BUILD_LOG"
|
||||
git apply hook-print.patch | tee -a "$BUILD_LOG"
|
||||
# FIXME: Remove once the JSC fixes around JSTag have landed in Safari, see
|
||||
# https://github.com/WebKit/JetStream/pull/84#issuecomment-3164672425.
|
||||
git apply jstag-workaround.patch | tee -a "$BUILD_LOG"
|
||||
cp $BUILD_SRC_DIR/skiko.{wasm,mjs} build/ | tee -a "$BUILD_LOG"
|
||||
git apply skiko-disable-instantiate.patch | tee -a "$BUILD_LOG"
|
||||
cp $BUILD_SRC_DIR/composeResources/compose_benchmarks.benchmarks.generated.resources/drawable/example1_cat.jpg build/ | tee -a "$BUILD_LOG"
|
||||
cp $BUILD_SRC_DIR/composeResources/compose_benchmarks.benchmarks.generated.resources/drawable/compose-multiplatform.png build/ | tee -a "$BUILD_LOG"
|
||||
cp $BUILD_SRC_DIR/composeResources/compose_benchmarks.benchmarks.generated.resources/files/example1_compose-community-primary.png build/ | tee -a "$BUILD_LOG"
|
||||
cp $BUILD_SRC_DIR/composeResources/compose_benchmarks.benchmarks.generated.resources/font/jetbrainsmono_*.ttf build/ | tee -a "$BUILD_LOG"
|
||||
echo "Build success" | tee -a "$BUILD_LOG"
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
|
||||
export async function instantiate(imports={}, runInitializer=true) {
|
||||
const cachedJsObjects = new WeakMap();
|
||||
// ref must be non-null
|
||||
function getCachedJsObject(ref, ifNotCached) {
|
||||
if (typeof ref !== 'object' && typeof ref !== 'function') return ifNotCached;
|
||||
const cached = cachedJsObjects.get(ref);
|
||||
if (cached !== void 0) return cached;
|
||||
cachedJsObjects.set(ref, ifNotCached);
|
||||
return ifNotCached;
|
||||
}
|
||||
|
||||
const _ref_Li9za2lrby5tanM_ = imports['./skiko.mjs'];
|
||||
const _ref_QGpzLWpvZGEvY29yZQ_ = imports['@js-joda/core'];
|
||||
|
||||
const js_code = {
|
||||
'kotlin.createJsError' : (message, cause) => new Error(message, { cause }),
|
||||
'kotlin.wasm.internal.stringLength' : (x) => x.length,
|
||||
'kotlin.wasm.internal.jsExportStringToWasm' : (src, srcOffset, srcLength, dstAddr) => {
|
||||
const mem16 = new Uint16Array(wasmExports.memory.buffer, dstAddr, srcLength);
|
||||
let arrayIndex = 0;
|
||||
let srcIndex = srcOffset;
|
||||
while (arrayIndex < srcLength) {
|
||||
mem16.set([src.charCodeAt(srcIndex)], arrayIndex);
|
||||
srcIndex++;
|
||||
arrayIndex++;
|
||||
}
|
||||
},
|
||||
'kotlin.wasm.internal.importStringFromWasm' : (address, length, prefix) => {
|
||||
const mem16 = new Uint16Array(wasmExports.memory.buffer, address, length);
|
||||
const str = String.fromCharCode.apply(null, mem16);
|
||||
return (prefix == null) ? str : prefix + str;
|
||||
},
|
||||
'kotlin.wasm.internal.externrefToBoolean' : (ref) => Boolean(ref),
|
||||
'kotlin.wasm.internal.intToExternref' : (x) => x,
|
||||
'kotlin.wasm.internal.getJsEmptyString' : () => '',
|
||||
'kotlin.wasm.internal.externrefToString' : (ref) => String(ref),
|
||||
'kotlin.wasm.internal.externrefEquals' : (lhs, rhs) => lhs === rhs,
|
||||
'kotlin.wasm.internal.externrefHashCode' :
|
||||
(() => {
|
||||
const dataView = new DataView(new ArrayBuffer(8));
|
||||
function numberHashCode(obj) {
|
||||
if ((obj | 0) === obj) {
|
||||
return obj | 0;
|
||||
} else {
|
||||
dataView.setFloat64(0, obj, true);
|
||||
return (dataView.getInt32(0, true) * 31 | 0) + dataView.getInt32(4, true) | 0;
|
||||
}
|
||||
}
|
||||
|
||||
const hashCodes = new WeakMap();
|
||||
function getObjectHashCode(obj) {
|
||||
const res = hashCodes.get(obj);
|
||||
if (res === undefined) {
|
||||
const POW_2_32 = 4294967296;
|
||||
const hash = (Math.random() * POW_2_32) | 0;
|
||||
hashCodes.set(obj, hash);
|
||||
return hash;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function getStringHashCode(str) {
|
||||
var hash = 0;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var code = str.charCodeAt(i);
|
||||
hash = (hash * 31 + code) | 0;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
return (obj) => {
|
||||
if (obj == null) {
|
||||
return 0;
|
||||
}
|
||||
switch (typeof obj) {
|
||||
case "object":
|
||||
case "function":
|
||||
return getObjectHashCode(obj);
|
||||
case "number":
|
||||
return numberHashCode(obj);
|
||||
case "boolean":
|
||||
return obj ? 1231 : 1237;
|
||||
default:
|
||||
return getStringHashCode(String(obj));
|
||||
}
|
||||
}
|
||||
})(),
|
||||
'kotlin.wasm.internal.isNullish' : (ref) => ref == null,
|
||||
'kotlin.wasm.internal.externrefToInt' : (ref) => Number(ref),
|
||||
'kotlin.wasm.internal.getJsTrue' : () => true,
|
||||
'kotlin.wasm.internal.getJsFalse' : () => false,
|
||||
'kotlin.wasm.internal.newJsArray' : () => [],
|
||||
'kotlin.wasm.internal.jsArrayPush' : (array, element) => { array.push(element); },
|
||||
'kotlin.wasm.internal.getCachedJsObject_$external_fun' : (p0, p1) => getCachedJsObject(p0, p1),
|
||||
'kotlin.js.jsCatch' : (f) => {
|
||||
let result = null;
|
||||
try {
|
||||
f();
|
||||
} catch (e) {
|
||||
result = e;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
'kotlin.js.__convertKotlinClosureToJsClosure_(()->Unit)' : (f) => getCachedJsObject(f, () => wasmExports['__callFunction_(()->Unit)'](f, )),
|
||||
'kotlin.js.jsThrow' : (e) => { throw e; },
|
||||
'kotlin.io.printlnImpl' : (message) => print(message),
|
||||
'kotlin.io.printError' : (error) => printErr(error),
|
||||
'kotlin.js.jsArrayGet' : (array, index) => array[index],
|
||||
'kotlin.js.jsArraySet' : (array, index, value) => { array[index] = value },
|
||||
'kotlin.js.JsArray_$external_fun' : () => new Array(),
|
||||
'kotlin.js.length_$external_prop_getter' : (_this) => _this.length,
|
||||
'kotlin.js.JsArray_$external_class_instanceof' : (x) => x instanceof Array,
|
||||
'kotlin.js.JsArray_$external_class_get' : () => Array,
|
||||
'kotlin.js.stackPlaceHolder_js_code' : () => (''),
|
||||
'kotlin.js.message_$external_prop_getter' : (_this) => _this.message,
|
||||
'kotlin.js.name_$external_prop_setter' : (_this, v) => _this.name = v,
|
||||
'kotlin.js.stack_$external_prop_getter' : (_this) => _this.stack,
|
||||
'kotlin.js.kotlinException_$external_prop_getter' : (_this) => _this.kotlinException,
|
||||
'kotlin.js.kotlinException_$external_prop_setter' : (_this, v) => _this.kotlinException = v,
|
||||
'kotlin.js.JsError_$external_class_instanceof' : (x) => x instanceof Error,
|
||||
'kotlin.js.JsString_$external_class_instanceof' : (x) => typeof x === 'string',
|
||||
'kotlin.js.JsString_$external_class_get' : () => JsString,
|
||||
'kotlin.js.Promise_$external_fun' : (p0) => new Promise(p0),
|
||||
'kotlin.js.__callJsClosure_((Js?)->Unit)' : (f, p0) => f(p0),
|
||||
'kotlin.js.__callJsClosure_((Js)->Unit)' : (f, p0) => f(p0),
|
||||
'kotlin.js.__convertKotlinClosureToJsClosure_((((Js?)->Unit),((Js)->Unit))->Unit)' : (f) => getCachedJsObject(f, (p0, p1) => wasmExports['__callFunction_((((Js?)->Unit),((Js)->Unit))->Unit)'](f, p0, p1)),
|
||||
'kotlin.js.then_$external_fun' : (_this, p0) => _this.then(p0),
|
||||
'kotlin.js.__convertKotlinClosureToJsClosure_((Js?)->Js?)' : (f) => getCachedJsObject(f, (p0) => wasmExports['__callFunction_((Js?)->Js?)'](f, p0)),
|
||||
'kotlin.js.then_$external_fun_1' : (_this, p0, p1) => _this.then(p0, p1),
|
||||
'kotlin.js.__convertKotlinClosureToJsClosure_((Js)->Js?)' : (f) => getCachedJsObject(f, (p0) => wasmExports['__callFunction_((Js)->Js?)'](f, p0)),
|
||||
'kotlin.js.catch_$external_fun' : (_this, p0) => _this.catch(p0),
|
||||
'kotlin.js.Promise_$external_class_instanceof' : (x) => x instanceof Promise,
|
||||
'kotlin.js.Promise_$external_class_get' : () => Promise,
|
||||
'kotlin.random.initialSeed' : () => ((Math.random() * Math.pow(2, 32)) | 0),
|
||||
'kotlin.wasm.internal.getJsClassName' : (jsKlass) => jsKlass.name,
|
||||
'kotlin.wasm.internal.instanceOf' : (ref, jsKlass) => ref instanceof jsKlass,
|
||||
'kotlin.wasm.internal.getConstructor' : (obj) => obj.constructor,
|
||||
'kotlin.time.tryGetPerformance' : () => typeof globalThis !== 'undefined' && typeof globalThis.performance !== 'undefined' ? globalThis.performance : null,
|
||||
'kotlin.time.getPerformanceNow' : (performance) => performance.now(),
|
||||
'kotlin.time.dateNow' : () => Date.now(),
|
||||
'kotlinx.browser.window_$external_prop_getter' : () => window,
|
||||
'kotlinx.browser.document_$external_prop_getter' : () => document,
|
||||
'org.w3c.dom.length_$external_prop_getter' : (_this) => _this.length,
|
||||
'org.khronos.webgl.getMethodImplForInt8Array' : (obj, index) => { return obj[index]; },
|
||||
'org.khronos.webgl.getMethodImplForUint8Array' : (obj, index) => { return obj[index]; },
|
||||
'org.khronos.webgl.slice_$external_fun' : (_this, p0, p1, isDefault0) => _this.slice(p0, isDefault0 ? undefined : p1, ),
|
||||
'org.khronos.webgl.Int8Array_$external_fun' : (p0, p1, p2, isDefault0, isDefault1) => new Int8Array(p0, isDefault0 ? undefined : p1, isDefault1 ? undefined : p2, ),
|
||||
'org.khronos.webgl.length_$external_prop_getter' : (_this) => _this.length,
|
||||
'org.khronos.webgl.Uint8Array_$external_fun' : (p0, p1, p2, isDefault0, isDefault1) => new Uint8Array(p0, isDefault0 ? undefined : p1, isDefault1 ? undefined : p2, ),
|
||||
'org.khronos.webgl.length_$external_prop_getter_1' : (_this) => _this.length,
|
||||
'org.khronos.webgl.buffer_$external_prop_getter' : (_this) => _this.buffer,
|
||||
'org.khronos.webgl.byteOffset_$external_prop_getter' : (_this) => _this.byteOffset,
|
||||
'org.khronos.webgl.byteLength_$external_prop_getter' : (_this) => _this.byteLength,
|
||||
'org.w3c.dom.clipboard.clipboardData_$external_prop_getter' : (_this) => _this.clipboardData,
|
||||
'org.w3c.dom.clipboard.ClipboardEvent_$external_class_instanceof' : (x) => x instanceof ClipboardEvent,
|
||||
'org.w3c.dom.events.__convertKotlinClosureToJsClosure_((Js)->Unit)' : (f) => getCachedJsObject(f, (p0) => wasmExports['__callFunction_((Js)->Unit)'](f, p0)),
|
||||
'org.w3c.dom.events.addEventListener_$external_fun' : (_this, p0, p1) => _this.addEventListener(p0, p1),
|
||||
'org.w3c.dom.events.addEventListener_$external_fun_1' : (_this, p0, p1) => _this.addEventListener(p0, p1),
|
||||
'org.w3c.dom.events.removeEventListener_$external_fun' : (_this, p0, p1) => _this.removeEventListener(p0, p1),
|
||||
'org.w3c.dom.events.removeEventListener_$external_fun_1' : (_this, p0, p1) => _this.removeEventListener(p0, p1),
|
||||
'org.w3c.dom.events.type_$external_prop_getter' : (_this) => _this.type,
|
||||
'org.w3c.dom.events.preventDefault_$external_fun' : (_this, ) => _this.preventDefault(),
|
||||
'org.w3c.dom.events.Event_$external_class_instanceof' : (x) => x instanceof Event,
|
||||
'org.w3c.dom.events.Event_$external_class_get' : () => Event,
|
||||
'org.w3c.dom.events.key_$external_prop_getter' : (_this) => _this.key,
|
||||
'org.w3c.dom.events.KeyboardEvent_$external_class_instanceof' : (x) => x instanceof KeyboardEvent,
|
||||
'org.w3c.dom.location_$external_prop_getter' : (_this) => _this.location,
|
||||
'org.w3c.dom.navigator_$external_prop_getter' : (_this) => _this.navigator,
|
||||
'org.w3c.dom.devicePixelRatio_$external_prop_getter' : (_this) => _this.devicePixelRatio,
|
||||
'org.w3c.dom.matchMedia_$external_fun' : (_this, p0) => _this.matchMedia(p0),
|
||||
'org.w3c.dom.matches_$external_prop_getter' : (_this) => _this.matches,
|
||||
'org.w3c.dom.protocol_$external_prop_getter' : (_this) => _this.protocol,
|
||||
'org.w3c.dom.hostname_$external_prop_getter' : (_this) => _this.hostname,
|
||||
'org.w3c.dom.getData_$external_fun' : (_this, p0) => _this.getData(p0),
|
||||
'org.w3c.dom.setData_$external_fun' : (_this, p0, p1) => _this.setData(p0, p1),
|
||||
'org.w3c.dom.language_$external_prop_getter' : (_this) => _this.language,
|
||||
'org.w3c.dom.clearTimeout_$external_fun' : (_this, p0, isDefault0) => _this.clearTimeout(isDefault0 ? undefined : p0, ),
|
||||
'org.w3c.dom.fetch_$external_fun' : (_this, p0, p1, isDefault0) => _this.fetch(p0, isDefault0 ? undefined : p1, ),
|
||||
'org.w3c.dom.documentElement_$external_prop_getter' : (_this) => _this.documentElement,
|
||||
'org.w3c.dom.namespaceURI_$external_prop_getter' : (_this) => _this.namespaceURI,
|
||||
'org.w3c.dom.localName_$external_prop_getter' : (_this) => _this.localName,
|
||||
'org.w3c.dom.getAttribute_$external_fun' : (_this, p0) => _this.getAttribute(p0),
|
||||
'org.w3c.dom.getAttributeNS_$external_fun' : (_this, p0, p1) => _this.getAttributeNS(p0, p1),
|
||||
'org.w3c.dom.Element_$external_class_instanceof' : (x) => x instanceof Element,
|
||||
'org.w3c.dom.data_$external_prop_getter' : (_this) => _this.data,
|
||||
'org.w3c.dom.nodeName_$external_prop_getter' : (_this) => _this.nodeName,
|
||||
'org.w3c.dom.childNodes_$external_prop_getter' : (_this) => _this.childNodes,
|
||||
'org.w3c.dom.lookupPrefix_$external_fun' : (_this, p0) => _this.lookupPrefix(p0),
|
||||
'org.w3c.dom.item_$external_fun' : (_this, p0) => _this.item(p0),
|
||||
'org.w3c.dom.binaryType_$external_prop_setter' : (_this, v) => _this.binaryType = v,
|
||||
'org.w3c.dom.close_$external_fun' : (_this, p0, p1, isDefault0, isDefault1) => _this.close(isDefault0 ? undefined : p0, isDefault1 ? undefined : p1, ),
|
||||
'org.w3c.dom.send_$external_fun' : (_this, p0) => _this.send(p0),
|
||||
'org.w3c.dom.send_$external_fun_1' : (_this, p0) => _this.send(p0),
|
||||
'org.w3c.dom.Companion_$external_object_getInstance' : () => ({}),
|
||||
'org.w3c.dom.code_$external_prop_getter' : (_this) => _this.code,
|
||||
'org.w3c.dom.reason_$external_prop_getter' : (_this) => _this.reason,
|
||||
'org.w3c.dom.parsing.DOMParser_$external_fun' : () => new DOMParser(),
|
||||
'org.w3c.dom.parsing.parseFromString_$external_fun' : (_this, p0, p1) => _this.parseFromString(p0, p1),
|
||||
'org.w3c.fetch.status_$external_prop_getter' : (_this) => _this.status,
|
||||
'org.w3c.fetch.ok_$external_prop_getter' : (_this) => _this.ok,
|
||||
'org.w3c.fetch.statusText_$external_prop_getter' : (_this) => _this.statusText,
|
||||
'org.w3c.fetch.headers_$external_prop_getter' : (_this) => _this.headers,
|
||||
'org.w3c.fetch.body_$external_prop_getter' : (_this) => _this.body,
|
||||
'org.w3c.fetch.blob_$external_fun' : (_this, ) => _this.blob(),
|
||||
'org.w3c.fetch.get_$external_fun' : (_this, p0) => _this.get(p0),
|
||||
'org.w3c.performance.now_$external_fun' : (_this, ) => _this.now(),
|
||||
'org.w3c.performance.performance_$external_prop_getter' : (_this) => _this.performance,
|
||||
'org.w3c.xhr.XMLHttpRequest_$external_fun' : () => new XMLHttpRequest(),
|
||||
'org.w3c.xhr.status_$external_prop_getter' : (_this) => _this.status,
|
||||
'org.w3c.xhr.open_$external_fun' : (_this, p0, p1, p2, p3, p4, isDefault0, isDefault1) => _this.open(p0, p1, p2, isDefault0 ? undefined : p3, isDefault1 ? undefined : p4, ),
|
||||
'org.w3c.xhr.send_$external_fun' : (_this, ) => _this.send(),
|
||||
'org.w3c.xhr.overrideMimeType_$external_fun' : (_this, p0) => _this.overrideMimeType(p0),
|
||||
'kotlinx.coroutines.tryGetProcess' : () => (typeof(process) !== 'undefined' && typeof(process.nextTick) === 'function') ? process : null,
|
||||
'kotlinx.coroutines.tryGetWindow' : () => (typeof(window) !== 'undefined' && window != null && typeof(window.addEventListener) === 'function') ? window : null,
|
||||
'kotlinx.coroutines.nextTick_$external_fun' : (_this, p0) => _this.nextTick(p0),
|
||||
'kotlinx.coroutines.error_$external_fun' : (_this, p0) => _this.error(p0),
|
||||
'kotlinx.coroutines.console_$external_prop_getter' : () => console,
|
||||
'kotlinx.coroutines.createScheduleMessagePoster' : (process) => () => Promise.resolve(0).then(process),
|
||||
'kotlinx.coroutines.__callJsClosure_(()->Unit)' : (f, ) => f(),
|
||||
'kotlinx.coroutines.createRescheduleMessagePoster' : (window) => () => window.postMessage('dispatchCoroutine', '*'),
|
||||
'kotlinx.coroutines.subscribeToWindowMessages' : (window, process) => {
|
||||
const handler = (event) => {
|
||||
if (event.source == window && event.data == 'dispatchCoroutine') {
|
||||
event.stopPropagation();
|
||||
process();
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', handler, true);
|
||||
},
|
||||
'kotlinx.coroutines.setTimeout' : (window, handler, timeout) => window.setTimeout(handler, timeout),
|
||||
'kotlinx.coroutines.clearTimeout' : (handle) => { if (typeof clearTimeout !== 'undefined') clearTimeout(handle); },
|
||||
'kotlinx.coroutines.setTimeout_$external_fun' : (p0, p1) => setTimeout(p0, p1),
|
||||
'kotlinx.coroutines.promiseSetDeferred' : (promise, deferred) => promise.deferred = deferred,
|
||||
'androidx.compose.runtime.internal.weakMap_js_code' : () => (new WeakMap()),
|
||||
'androidx.compose.runtime.internal.set_$external_fun' : (_this, p0, p1) => _this.set(p0, p1),
|
||||
'androidx.compose.runtime.internal.get_$external_fun' : (_this, p0) => _this.get(p0),
|
||||
'org.jetbrains.skiko.w3c.language_$external_prop_getter' : (_this) => _this.language,
|
||||
'org.jetbrains.skiko.w3c.userAgent_$external_prop_getter' : (_this) => _this.userAgent,
|
||||
'org.jetbrains.skiko.w3c.navigator_$external_prop_getter' : (_this) => _this.navigator,
|
||||
'org.jetbrains.skiko.w3c.performance_$external_prop_getter' : (_this) => _this.performance,
|
||||
'org.jetbrains.skiko.w3c.open_$external_fun' : (_this, p0, p1) => _this.open(p0, p1),
|
||||
'org.jetbrains.skiko.w3c.window_$external_object_getInstance' : () => window,
|
||||
'org.jetbrains.skiko.w3c.now_$external_fun' : (_this, ) => _this.now(),
|
||||
'org.jetbrains.skia.impl.FinalizationRegistry_$external_fun' : (p0) => new FinalizationRegistry(p0),
|
||||
'org.jetbrains.skia.impl.register_$external_fun' : (_this, p0, p1, p2) => _this.register(p0, p1, p2),
|
||||
'org.jetbrains.skia.impl.unregister_$external_fun' : (_this, p0) => _this.unregister(p0),
|
||||
'org.jetbrains.skia.impl._releaseLocalCallbackScope_$external_fun' : () => _ref_Li9za2lrby5tanM_._releaseLocalCallbackScope(),
|
||||
'org.jetbrains.skiko.getNavigatorInfo' : () => navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform,
|
||||
'androidx.compose.ui.text.intl.getUserPreferredLanguagesAsArray' : () => window.navigator.languages,
|
||||
'androidx.compose.ui.text.intl.parseLanguageTagToIntlLocale' : (languageTag) => new Intl.Locale(languageTag),
|
||||
'androidx.compose.ui.text.intl._language_$external_prop_getter' : (_this) => _this.language,
|
||||
'androidx.compose.ui.text.intl._region_$external_prop_getter' : (_this) => _this.region,
|
||||
'androidx.compose.ui.text.intl._baseName_$external_prop_getter' : (_this) => _this.baseName,
|
||||
'androidx.compose.ui.internal.weakMap_js_code' : () => (new WeakMap()),
|
||||
'androidx.compose.ui.internal.set_$external_fun' : (_this, p0, p1) => _this.set(p0, p1),
|
||||
'androidx.compose.ui.internal.get_$external_fun' : (_this, p0) => _this.get(p0),
|
||||
'androidx.compose.ui.platform.isSecureContext' : () => window.isSecureContext,
|
||||
'androidx.compose.ui.platform.invalidClipboardItems' : () => [],
|
||||
'androidx.compose.ui.platform.createClipboardItemWithPlainText' : (text) => [new ClipboardItem({'text/plain': new Blob([text], { type: 'text/plain' })})],
|
||||
'androidx.compose.ui.platform.getW3CClipboard' : () => window.navigator.clipboard,
|
||||
'androidx.compose.ui.platform.emptyClipboardItems' : () => [new ClipboardItem({'text/plain': new Blob([''], { type: 'text/plain' })})],
|
||||
'androidx.compose.ui.platform.read_$external_fun' : (_this, ) => _this.read(),
|
||||
'androidx.compose.ui.platform.write_$external_fun' : (_this, p0) => _this.write(p0),
|
||||
'androidx.compose.ui.platform.W3CTemporaryClipboard_$external_class_instanceof' : (x) => x instanceof Clipboard,
|
||||
'androidx.compose.ui.platform.W3CTemporaryClipboard_$external_class_get' : () => Clipboard,
|
||||
'androidx.compose.ui.platform.types_$external_prop_getter' : (_this) => _this.types,
|
||||
'androidx.compose.ui.platform.getType_$external_fun' : (_this, p0) => _this.getType(p0),
|
||||
'androidx.compose.foundation.internal.doesJsArrayContainValue' : (jsArray, value) => jsArray.includes(value),
|
||||
'androidx.compose.foundation.internal.getTextFromBlob' : (blob) => blob.text(),
|
||||
'androidx.compose.foundation.text.EventListener' : (handler) => (event) => { handler(event) },
|
||||
'androidx.compose.material.internal.weakMap_js_code' : () => (new WeakMap()),
|
||||
'androidx.compose.material.internal.set_$external_fun' : (_this, p0, p1) => _this.set(p0, p1),
|
||||
'androidx.compose.material.internal.get_$external_fun' : (_this, p0) => _this.get(p0),
|
||||
'org.jetbrains.compose.resources.Locale_$external_fun' : (p0) => new Intl.Locale(p0),
|
||||
'org.jetbrains.compose.resources.language_$external_prop_getter' : (_this) => _this.language,
|
||||
'org.jetbrains.compose.resources.region_$external_prop_getter' : (_this) => _this.region,
|
||||
'org.jetbrains.compose.resources.jsExportBlobAsArrayBuffer' : (blob) => blob.arrayBuffer(),
|
||||
'org.jetbrains.compose.resources.jsExportInt8ArrayToWasm' : (src, size, dstAddr) => {
|
||||
const mem8 = new Int8Array(wasmExports.memory.buffer, dstAddr, size);
|
||||
mem8.set(src);
|
||||
}
|
||||
,
|
||||
'org.jetbrains.compose.resources.requestResponseAsByteArray' : (req) => {
|
||||
var text = req.responseText;
|
||||
var int8Arr = new Int8Array(text.length);
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
int8Arr[i] = text.charCodeAt(i) & 0xFF;
|
||||
}
|
||||
return int8Arr;
|
||||
},
|
||||
'org.jetbrains.compose.resources.isInTestEnvironment' : () => window.composeResourcesTesting == true,
|
||||
'io.ktor.utils.io.js.decode' : (decoder) => { try { return decoder.decode() } catch(e) { return null } },
|
||||
'io.ktor.utils.io.js.decode_1' : (decoder, buffer) => { try { return decoder.decode(buffer) } catch(e) { return null } },
|
||||
'io.ktor.utils.io.js.decodeStream' : (decoder, buffer) => { try { return decoder.decode(buffer, { stream: true }) } catch(e) { return null } },
|
||||
'io.ktor.utils.io.js.tryCreateTextDecoder' : (encoding, fatal) => { try { return new TextDecoder(encoding, { fatal: fatal }) } catch(e) { return null } },
|
||||
'io.ktor.utils.io.js.TextEncoder_$external_fun' : () => new TextEncoder(),
|
||||
'io.ktor.utils.io.js.encode_$external_fun' : (_this, p0) => _this.encode(p0),
|
||||
'io.ktor.utils.io.js.toJsArrayImpl' : (x) => new Int8Array(x),
|
||||
'io.ktor.util.hasNodeApi' : () =>
|
||||
(typeof process !== 'undefined'
|
||||
&& process.versions != null
|
||||
&& process.versions.node != null) ||
|
||||
(typeof window !== 'undefined'
|
||||
&& typeof window.process !== 'undefined'
|
||||
&& window.process.versions != null
|
||||
&& window.process.versions.node != null)
|
||||
,
|
||||
'io.ktor.util.logging.getKtorLogLevel' : () => process.env.KTOR_LOG_LEVEL,
|
||||
'io.ktor.util.logging.debug_$external_fun' : (_this, p0) => _this.debug(p0),
|
||||
'io.ktor.util.logging.console_$external_prop_getter' : () => console,
|
||||
'io.ktor.util.date.Date_$external_fun' : () => new Date(),
|
||||
'io.ktor.util.date.Date_$external_fun_1' : (p0) => new Date(p0),
|
||||
'io.ktor.util.date.getTime_$external_fun' : (_this, ) => _this.getTime(),
|
||||
'io.ktor.util.date.getUTCDate_$external_fun' : (_this, ) => _this.getUTCDate(),
|
||||
'io.ktor.util.date.getUTCDay_$external_fun' : (_this, ) => _this.getUTCDay(),
|
||||
'io.ktor.util.date.getUTCFullYear_$external_fun' : (_this, ) => _this.getUTCFullYear(),
|
||||
'io.ktor.util.date.getUTCHours_$external_fun' : (_this, ) => _this.getUTCHours(),
|
||||
'io.ktor.util.date.getUTCMinutes_$external_fun' : (_this, ) => _this.getUTCMinutes(),
|
||||
'io.ktor.util.date.getUTCMonth_$external_fun' : (_this, ) => _this.getUTCMonth(),
|
||||
'io.ktor.util.date.getUTCSeconds_$external_fun' : (_this, ) => _this.getUTCSeconds(),
|
||||
'io.ktor.http.locationOrigin' : () => function() {
|
||||
var origin = ""
|
||||
if (typeof window !== 'undefined') {
|
||||
origin = window.location.origin
|
||||
} else {
|
||||
origin = self.location.origin
|
||||
}
|
||||
return origin && origin != "null" ? origin : "http://localhost"
|
||||
}(),
|
||||
'io.ktor.client.engine.js.createBrowserWebSocket' : (urlString_capturingHack, protocols) => new WebSocket(urlString_capturingHack, protocols),
|
||||
'io.ktor.client.engine.js.createWebSocketNodeJs' : (socketCtor, urlString_capturingHack, headers_capturingHack, protocols) => new socketCtor(urlString_capturingHack, protocols, { headers: headers_capturingHack }),
|
||||
'io.ktor.client.engine.js.getKeys' : (headers) => Array.from(headers.keys()),
|
||||
'io.ktor.client.engine.js.eventAsString' : (event) => JSON.stringify(event, ['message', 'target', 'type', 'isTrusted']),
|
||||
'io.ktor.client.engine.js.compatibility.abortControllerCtorBrowser' : () => AbortController,
|
||||
'io.ktor.client.engine.js.node.bodyOn' : (body, type, handler) => body.on(type, handler),
|
||||
'io.ktor.client.engine.js.node.bodyOn_1' : (body, type, handler) => body.on(type, handler),
|
||||
'io.ktor.client.engine.js.node.pause_$external_fun' : (_this, ) => _this.pause(),
|
||||
'io.ktor.client.engine.js.node.resume_$external_fun' : (_this, ) => _this.resume(),
|
||||
'io.ktor.client.engine.js.node.destroy_$external_fun' : (_this, p0) => _this.destroy(p0),
|
||||
'io.ktor.client.fetch.signal_$external_prop_setter' : (_this, v) => _this.signal = v,
|
||||
'io.ktor.client.fetch.signal_$external_prop_getter' : (_this) => _this.signal,
|
||||
'io.ktor.client.fetch.abort_$external_fun' : (_this, ) => _this.abort(),
|
||||
'io.ktor.client.fetch.fetch_$external_fun' : (p0, p1, isDefault0) => fetch(p0, isDefault0 ? undefined : p1, ),
|
||||
'io.ktor.client.fetch.getReader_$external_fun' : (_this, ) => _this.getReader(),
|
||||
'io.ktor.client.fetch.cancel_$external_fun' : (_this, p0, isDefault0) => _this.cancel(isDefault0 ? undefined : p0, ),
|
||||
'io.ktor.client.fetch.read_$external_fun' : (_this, ) => _this.read(),
|
||||
'io.ktor.client.fetch.done_$external_prop_getter' : (_this) => _this.done,
|
||||
'io.ktor.client.fetch.value_$external_prop_getter' : (_this) => _this.value,
|
||||
'io.ktor.client.plugins.websocket.tryGetEventDataAsString' : (data) => typeof(data) === 'string' ? data : null,
|
||||
'io.ktor.client.plugins.websocket.tryGetEventDataAsArrayBuffer' : (data) => data instanceof ArrayBuffer ? data : null,
|
||||
'io.ktor.client.utils.makeJsObject' : () => { return {}; },
|
||||
'io.ktor.client.utils.makeRequire' : (name) => require(name),
|
||||
'io.ktor.client.utils.makeJsCall' : (func, arg) => func.apply(null, arg),
|
||||
'io.ktor.client.utils.makeJsNew' : (ctor) => new ctor(),
|
||||
'io.ktor.client.utils.setObjectField' : (obj, name, value) => obj[name]=value,
|
||||
'io.ktor.client.utils.toJsArrayImpl' : (x) => new Uint8Array(x),
|
||||
'runGC' : () => { (typeof gc === 'function')? gc() : console.log('Manual GC is not available. Ensure that the browser was started with the appropriate flags.') }
|
||||
}
|
||||
|
||||
// Placed here to give access to it from externals (js_code)
|
||||
let wasmInstance;
|
||||
let require;
|
||||
let wasmExports;
|
||||
|
||||
const isNodeJs = (typeof process !== 'undefined') && (process.release.name === 'node');
|
||||
const isDeno = !isNodeJs && (typeof Deno !== 'undefined')
|
||||
const isStandaloneJsVM =
|
||||
!isDeno && !isNodeJs && (
|
||||
typeof d8 !== 'undefined' // V8
|
||||
|| typeof inIon !== 'undefined' // SpiderMonkey
|
||||
|| typeof jscOptions !== 'undefined' // JavaScriptCore
|
||||
);
|
||||
const isBrowser = !isNodeJs && !isDeno && !isStandaloneJsVM && (typeof window !== 'undefined' || typeof self !== 'undefined');
|
||||
|
||||
if (!isNodeJs && !isDeno && !isStandaloneJsVM && !isBrowser) {
|
||||
throw "Supported JS engine not detected";
|
||||
}
|
||||
|
||||
const wasmFilePath = './compose-benchmarks-benchmarks.wasm';
|
||||
|
||||
const wasmTag = new WebAssembly.Tag({ parameters: ['externref'] });
|
||||
|
||||
const importObject = {
|
||||
js_code,
|
||||
intrinsics: {
|
||||
tag: wasmTag
|
||||
},
|
||||
'./skiko.mjs': imports['./skiko.mjs'],
|
||||
|
||||
};
|
||||
|
||||
try {
|
||||
if (isNodeJs) {
|
||||
const module = await import(/* webpackIgnore: true */'node:module');
|
||||
const importMeta = import.meta;
|
||||
require = module.default.createRequire(importMeta.url);
|
||||
const fs = require('fs');
|
||||
const url = require('url');
|
||||
const filepath = import.meta.resolve(wasmFilePath);
|
||||
const wasmBuffer = fs.readFileSync(url.fileURLToPath(filepath));
|
||||
const wasmModule = new WebAssembly.Module(wasmBuffer);
|
||||
wasmInstance = new WebAssembly.Instance(wasmModule, importObject);
|
||||
}
|
||||
|
||||
if (isDeno) {
|
||||
const path = await import(/* webpackIgnore: true */'https://deno.land/std/path/mod.ts');
|
||||
const binary = Deno.readFileSync(path.fromFileUrl(import.meta.resolve(wasmFilePath)));
|
||||
const module = await WebAssembly.compile(binary);
|
||||
wasmInstance = await WebAssembly.instantiate(module, importObject);
|
||||
}
|
||||
|
||||
if (isStandaloneJsVM) {
|
||||
const wasmBuffer = read(wasmFilePath, 'binary');
|
||||
const wasmModule = new WebAssembly.Module(wasmBuffer);
|
||||
wasmInstance = new WebAssembly.Instance(wasmModule, importObject);
|
||||
}
|
||||
|
||||
if (isBrowser) {
|
||||
wasmInstance = (await WebAssembly.instantiateStreaming(fetch(new URL('./compose-benchmarks-benchmarks.wasm',import.meta.url).href), importObject)).instance;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof WebAssembly.CompileError) {
|
||||
let text = `Please make sure that your runtime environment supports the latest version of Wasm GC and Exception-Handling proposals.
|
||||
For more information, see https://kotl.in/wasm-help
|
||||
`;
|
||||
if (isBrowser) {
|
||||
console.error(text);
|
||||
} else {
|
||||
const t = "\n" + text;
|
||||
if (typeof console !== "undefined" && console.log !== void 0)
|
||||
console.log(t);
|
||||
else
|
||||
print(t);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
wasmExports = wasmInstance.exports;
|
||||
if (runInitializer) {
|
||||
wasmExports._initialize();
|
||||
}
|
||||
|
||||
return { instance: wasmInstance, exports: wasmExports };
|
||||
}
|
||||
BIN
Kotlin-compose/build/compose-benchmarks-benchmarks.wasm
Normal file
BIN
Kotlin-compose/build/compose-benchmarks-benchmarks.wasm
Normal file
Binary file not shown.
BIN
Kotlin-compose/build/compose-multiplatform.png
Normal file
BIN
Kotlin-compose/build/compose-multiplatform.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
Kotlin-compose/build/example1_cat.jpg
Normal file
BIN
Kotlin-compose/build/example1_cat.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
Kotlin-compose/build/example1_compose-community-primary.png
Normal file
BIN
Kotlin-compose/build/example1_compose-community-primary.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
Kotlin-compose/build/jetbrainsmono_italic.ttf
Normal file
BIN
Kotlin-compose/build/jetbrainsmono_italic.ttf
Normal file
Binary file not shown.
BIN
Kotlin-compose/build/jetbrainsmono_regular.ttf
Normal file
BIN
Kotlin-compose/build/jetbrainsmono_regular.ttf
Normal file
Binary file not shown.
9299
Kotlin-compose/build/skiko.mjs
Normal file
9299
Kotlin-compose/build/skiko.mjs
Normal file
File diff suppressed because it is too large
Load diff
BIN
Kotlin-compose/build/skiko.wasm
Executable file
BIN
Kotlin-compose/build/skiko.wasm
Executable file
Binary file not shown.
39
Kotlin-compose/empty-main-function.patch
Normal file
39
Kotlin-compose/empty-main-function.patch
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
diff --git a/benchmarks/multiplatform/benchmarks/src/wasmJsMain/kotlin/main.wasmJs.kt b/benchmarks/multiplatform/benchmarks/src/wasmJsMain/kotlin/main.wasmJs.kt
|
||||
index d796d88975..f2c9e48eda 100644
|
||||
--- a/benchmarks/multiplatform/benchmarks/src/wasmJsMain/kotlin/main.wasmJs.kt
|
||||
+++ b/benchmarks/multiplatform/benchmarks/src/wasmJsMain/kotlin/main.wasmJs.kt
|
||||
@@ -8,11 +8,6 @@ import kotlin.js.Promise
|
||||
val jsOne = 1.toJsNumber()
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
- if (isD8env().toBoolean()) {
|
||||
- mainD8(args)
|
||||
- } else {
|
||||
- mainBrowser()
|
||||
- }
|
||||
}
|
||||
|
||||
fun mainBrowser() {
|
||||
@@ -37,15 +32,6 @@ fun mainBrowser() {
|
||||
}
|
||||
}
|
||||
|
||||
-
|
||||
-// Currently, the initialization can't be adjusted to avoid calling the fun main, but
|
||||
-// we don't want use the default fun main, because Jetstream3 requires running the workloads separately / independently of each other.
|
||||
-// Also, they require that a benchmark completes before the function exists, which is not possible with if they just call fun main.
|
||||
-// Therefore, they'll rely on fun customLaunch, which returns a Promise (can be awaited for).
|
||||
-fun mainD8(args: Array<String>) {
|
||||
- println("mainD8 is intentionally doing nothing. Read the comments in main.wasmJs.kt")
|
||||
-}
|
||||
-
|
||||
private val basicConfigForD8 = Config(
|
||||
// Using only SIMPLE mode, because VSYNC_EMULATION calls delay(...),
|
||||
// which is implemented via setTimeout on web targets.
|
||||
@@ -85,6 +71,3 @@ fun d8BenchmarksRunner(args: String): Promise<JsAny?> {
|
||||
jsOne
|
||||
}
|
||||
}
|
||||
-
|
||||
-private fun isD8env(): JsBoolean =
|
||||
- js("typeof isD8 !== 'undefined'")
|
||||
15
Kotlin-compose/hook-print.patch
Normal file
15
Kotlin-compose/hook-print.patch
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
diff --git a/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs b/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs
|
||||
index f92d6ec..54cc4cf 100644
|
||||
--- a/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs
|
||||
+++ b/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs
|
||||
@@ -111,8 +111,8 @@ export async function instantiate(imports={}, runInitializer=true) {
|
||||
},
|
||||
'kotlin.js.__convertKotlinClosureToJsClosure_(()->Unit)' : (f) => getCachedJsObject(f, () => wasmExports['__callFunction_(()->Unit)'](f, )),
|
||||
'kotlin.js.jsThrow' : (e) => { throw e; },
|
||||
- 'kotlin.io.printlnImpl' : (message) => console.log(message),
|
||||
- 'kotlin.io.printError' : (error) => console.error(error),
|
||||
+ 'kotlin.io.printlnImpl' : (message) => print(message),
|
||||
+ 'kotlin.io.printError' : (error) => printErr(error),
|
||||
'kotlin.js.jsArrayGet' : (array, index) => array[index],
|
||||
'kotlin.js.jsArraySet' : (array, index, value) => { array[index] = value },
|
||||
'kotlin.js.JsArray_$external_fun' : () => new Array(),
|
||||
13
Kotlin-compose/jstag-workaround.patch
Normal file
13
Kotlin-compose/jstag-workaround.patch
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
diff --git a/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs b/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs
|
||||
index 3427ca2..882308d 100644
|
||||
--- a/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs
|
||||
+++ b/Kotlin-compose/build/compose-benchmarks-benchmarks.uninstantiated.mjs
|
||||
@@ -383,7 +383,7 @@ export async function instantiate(imports={}, runInitializer=true) {
|
||||
|
||||
const wasmFilePath = './compose-benchmarks-benchmarks.wasm';
|
||||
|
||||
- const wasmTag = WebAssembly.JSTag ?? new WebAssembly.Tag({ parameters: ['externref'] });
|
||||
+ const wasmTag = new WebAssembly.Tag({ parameters: ['externref'] });
|
||||
|
||||
const importObject = {
|
||||
js_code,
|
||||
17
Kotlin-compose/skiko-disable-instantiate.patch
Normal file
17
Kotlin-compose/skiko-disable-instantiate.patch
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
diff --git a/Kotlin-compose/build/skiko.mjs b/Kotlin-compose/build/skiko.mjs
|
||||
index ca8cabd..157890b 100644
|
||||
--- a/Kotlin-compose/build/skiko.mjs
|
||||
+++ b/Kotlin-compose/build/skiko.mjs
|
||||
@@ -8325,6 +8325,7 @@ export const {
|
||||
_releaseLocalCallbackScope
|
||||
} = SkikoCallbacks;
|
||||
|
||||
+/*
|
||||
export const loadedWasm = await loadSkikoWASM();
|
||||
|
||||
export const { GL } = loadedWasm;
|
||||
@@ -9295,3 +9296,4 @@ export const {
|
||||
skia_memSetDouble,
|
||||
skia_memGetDouble,
|
||||
} = loadedWasm.wasmExports;
|
||||
+*/
|
||||
35
Kotlin-compose/use-beta-toolchain.patch
Normal file
35
Kotlin-compose/use-beta-toolchain.patch
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
diff --git a/benchmarks/multiplatform/build.gradle.kts b/benchmarks/multiplatform/build.gradle.kts
|
||||
index 96ae9a60c7..25de0aead1 100644
|
||||
--- a/benchmarks/multiplatform/build.gradle.kts
|
||||
+++ b/benchmarks/multiplatform/build.gradle.kts
|
||||
@@ -9,6 +9,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
+ maven("https://packages.jetbrains.team/maven/p/kt/dev/org/jetbrains/kotlin/kotlin-gradle-plugin/")
|
||||
google()
|
||||
mavenCentral()
|
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
diff --git a/benchmarks/multiplatform/gradle/libs.versions.toml b/benchmarks/multiplatform/gradle/libs.versions.toml
|
||||
index 8713cf0de7..da803c319b 100644
|
||||
--- a/benchmarks/multiplatform/gradle/libs.versions.toml
|
||||
+++ b/benchmarks/multiplatform/gradle/libs.versions.toml
|
||||
@@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
compose-multiplatform = "1.8.2"
|
||||
-kotlin = "2.2.0"
|
||||
+kotlin = "2.2.20-Beta2"
|
||||
kotlinx-coroutines = "1.8.0"
|
||||
kotlinx-serialization = "1.8.0"
|
||||
kotlinx-io = "0.7.0"
|
||||
diff --git a/benchmarks/multiplatform/settings.gradle.kts b/benchmarks/multiplatform/settings.gradle.kts
|
||||
index b1d1367534..84a894ed78 100644
|
||||
--- a/benchmarks/multiplatform/settings.gradle.kts
|
||||
+++ b/benchmarks/multiplatform/settings.gradle.kts
|
||||
@@ -1,5 +1,6 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
+ maven("https://packages.jetbrains.team/maven/p/kt/dev/org/jetbrains/kotlin/kotlin-gradle-plugin/")
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
|
@ -1 +0,0 @@
|
|||
print("Hello world!");
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
function firstWhere(list, fn) {
|
||||
for (var x of list) {
|
||||
if (fn(x)) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
nums = [1, 2, 3, 4, 5, 6, 7];
|
||||
function isEven(x) { return (x & 1) == 0; }
|
||||
firstEven = firstWhere(nums, isEven);
|
||||
print('First even: ' + firstEven)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
var n = 40000;
|
||||
var i = 0;
|
||||
var items = [];
|
||||
|
||||
while (i < n) {
|
||||
items.push(i);
|
||||
i = i + 1;
|
||||
}
|
||||
|
|
@ -1,537 +0,0 @@
|
|||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// This is a JavaScript implementation of the Richards
|
||||
// benchmark from:
|
||||
//
|
||||
// http://www.cl.cam.ac.uk/~mr10/Bench.html
|
||||
//
|
||||
// The benchmark was originally implemented in BCPL by
|
||||
// Martin Richards.
|
||||
|
||||
|
||||
/**
|
||||
* The Richards benchmark simulates the task dispatcher of an
|
||||
* operating system.
|
||||
**/
|
||||
function runRichards() {
|
||||
var scheduler = new Scheduler();
|
||||
scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
|
||||
|
||||
var queue = new Packet(null, ID_WORKER, KIND_WORK);
|
||||
queue = new Packet(queue, ID_WORKER, KIND_WORK);
|
||||
scheduler.addWorkerTask(ID_WORKER, 1000, queue);
|
||||
|
||||
queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
|
||||
scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
|
||||
|
||||
queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
|
||||
scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
|
||||
|
||||
scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
|
||||
|
||||
scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
|
||||
|
||||
scheduler.schedule();
|
||||
|
||||
if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
|
||||
scheduler.holdCount != EXPECTED_HOLD_COUNT) {
|
||||
var msg =
|
||||
"Error during execution: queueCount = " + scheduler.queueCount +
|
||||
", holdCount = " + scheduler.holdCount + ".";
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
var COUNT = 1000;
|
||||
|
||||
/**
|
||||
* These two constants specify how many times a packet is queued and
|
||||
* how many times a task is put on hold in a correct run of richards.
|
||||
* They don't have any meaning a such but are characteristic of a
|
||||
* correct run so if the actual queue or hold count is different from
|
||||
* the expected there must be a bug in the implementation.
|
||||
**/
|
||||
var EXPECTED_QUEUE_COUNT = 2322;
|
||||
var EXPECTED_HOLD_COUNT = 928;
|
||||
|
||||
|
||||
/**
|
||||
* A scheduler can be used to schedule a set of tasks based on their relative
|
||||
* priorities. Scheduling is done by maintaining a list of task control blocks
|
||||
* which holds tasks and the data queue they are processing.
|
||||
* @constructor
|
||||
*/
|
||||
function Scheduler() {
|
||||
this.queueCount = 0;
|
||||
this.holdCount = 0;
|
||||
this.blocks = new Array(NUMBER_OF_IDS);
|
||||
this.list = null;
|
||||
this.currentTcb = null;
|
||||
this.currentId = null;
|
||||
}
|
||||
|
||||
var ID_IDLE = 0;
|
||||
var ID_WORKER = 1;
|
||||
var ID_HANDLER_A = 2;
|
||||
var ID_HANDLER_B = 3;
|
||||
var ID_DEVICE_A = 4;
|
||||
var ID_DEVICE_B = 5;
|
||||
var NUMBER_OF_IDS = 6;
|
||||
|
||||
var KIND_DEVICE = 0;
|
||||
var KIND_WORK = 1;
|
||||
|
||||
/**
|
||||
* Add an idle task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {int} count the number of times to schedule the task
|
||||
*/
|
||||
Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
|
||||
this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a work task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new HandlerTask(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new DeviceTask(this))
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified task and mark it as running.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {Task} task the task to add
|
||||
*/
|
||||
Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
|
||||
this.addTask(id, priority, queue, task);
|
||||
this.currentTcb.setRunning();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {Task} task the task to add
|
||||
*/
|
||||
Scheduler.prototype.addTask = function (id, priority, queue, task) {
|
||||
this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
|
||||
this.list = this.currentTcb;
|
||||
this.blocks[id] = this.currentTcb;
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the tasks managed by this scheduler.
|
||||
*/
|
||||
Scheduler.prototype.schedule = function () {
|
||||
this.currentTcb = this.list;
|
||||
while (this.currentTcb != null) {
|
||||
if (this.currentTcb.isHeldOrSuspended()) {
|
||||
this.currentTcb = this.currentTcb.link;
|
||||
} else {
|
||||
this.currentId = this.currentTcb.id;
|
||||
this.currentTcb = this.currentTcb.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Release a task that is currently blocked and return the next block to run.
|
||||
* @param {int} id the id of the task to suspend
|
||||
*/
|
||||
Scheduler.prototype.release = function (id) {
|
||||
var tcb = this.blocks[id];
|
||||
if (tcb == null) return tcb;
|
||||
tcb.markAsNotHeld();
|
||||
if (tcb.priority > this.currentTcb.priority) {
|
||||
return tcb;
|
||||
} else {
|
||||
return this.currentTcb;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Block the currently executing task and return the next task control block
|
||||
* to run. The blocked task will not be made runnable until it is explicitly
|
||||
* released, even if new work is added to it.
|
||||
*/
|
||||
Scheduler.prototype.holdCurrent = function () {
|
||||
this.holdCount++;
|
||||
this.currentTcb.markAsHeld();
|
||||
return this.currentTcb.link;
|
||||
};
|
||||
|
||||
/**
|
||||
* Suspend the currently executing task and return the next task control block
|
||||
* to run. If new work is added to the suspended task it will be made runnable.
|
||||
*/
|
||||
Scheduler.prototype.suspendCurrent = function () {
|
||||
this.currentTcb.markAsSuspended();
|
||||
return this.currentTcb;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified packet to the end of the worklist used by the task
|
||||
* associated with the packet and make the task runnable if it is currently
|
||||
* suspended.
|
||||
* @param {Packet} packet the packet to add
|
||||
*/
|
||||
Scheduler.prototype.queue = function (packet) {
|
||||
var t = this.blocks[packet.id];
|
||||
if (t == null) return t;
|
||||
this.queueCount++;
|
||||
packet.link = null;
|
||||
packet.id = this.currentId;
|
||||
return t.checkPriorityAdd(this.currentTcb, packet);
|
||||
};
|
||||
|
||||
/**
|
||||
* A task control block manages a task and the queue of work packages associated
|
||||
* with it.
|
||||
* @param {TaskControlBlock} link the preceding block in the linked block list
|
||||
* @param {int} id the id of this block
|
||||
* @param {int} priority the priority of this block
|
||||
* @param {Packet} queue the queue of packages to be processed by the task
|
||||
* @param {Task} task the task
|
||||
* @constructor
|
||||
*/
|
||||
function TaskControlBlock(link, id, priority, queue, task) {
|
||||
this.link = link;
|
||||
this.id = id;
|
||||
this.priority = priority;
|
||||
this.queue = queue;
|
||||
this.task = task;
|
||||
if (queue == null) {
|
||||
this.state = STATE_SUSPENDED;
|
||||
} else {
|
||||
this.state = STATE_SUSPENDED_RUNNABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The task is running and is currently scheduled.
|
||||
*/
|
||||
var STATE_RUNNING = 0;
|
||||
|
||||
/**
|
||||
* The task has packets left to process.
|
||||
*/
|
||||
var STATE_RUNNABLE = 1;
|
||||
|
||||
/**
|
||||
* The task is not currently running. The task is not blocked as such and may
|
||||
* be started by the scheduler.
|
||||
*/
|
||||
var STATE_SUSPENDED = 2;
|
||||
|
||||
/**
|
||||
* The task is blocked and cannot be run until it is explicitly released.
|
||||
*/
|
||||
var STATE_HELD = 4;
|
||||
|
||||
var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
|
||||
var STATE_NOT_HELD = ~STATE_HELD;
|
||||
|
||||
TaskControlBlock.prototype.setRunning = function () {
|
||||
this.state = STATE_RUNNING;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsNotHeld = function () {
|
||||
this.state = this.state & STATE_NOT_HELD;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsHeld = function () {
|
||||
this.state = this.state | STATE_HELD;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.isHeldOrSuspended = function () {
|
||||
return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsSuspended = function () {
|
||||
this.state = this.state | STATE_SUSPENDED;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsRunnable = function () {
|
||||
this.state = this.state | STATE_RUNNABLE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs this task, if it is ready to be run, and returns the next task to run.
|
||||
*/
|
||||
TaskControlBlock.prototype.run = function () {
|
||||
var packet;
|
||||
if (this.state == STATE_SUSPENDED_RUNNABLE) {
|
||||
packet = this.queue;
|
||||
this.queue = packet.link;
|
||||
if (this.queue == null) {
|
||||
this.state = STATE_RUNNING;
|
||||
} else {
|
||||
this.state = STATE_RUNNABLE;
|
||||
}
|
||||
} else {
|
||||
packet = null;
|
||||
}
|
||||
return this.task.run(packet);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a packet to the worklist of this block's task, marks this as runnable if
|
||||
* necessary, and returns the next runnable object to run (the one
|
||||
* with the highest priority).
|
||||
*/
|
||||
TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
|
||||
if (this.queue == null) {
|
||||
this.queue = packet;
|
||||
this.markAsRunnable();
|
||||
if (this.priority > task.priority) return this;
|
||||
} else {
|
||||
this.queue = packet.addTo(this.queue);
|
||||
}
|
||||
return task;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.toString = function () {
|
||||
return "tcb { " + this.task + "@" + this.state + " }";
|
||||
};
|
||||
|
||||
/**
|
||||
* An idle task doesn't do any work itself but cycles control between the two
|
||||
* device tasks.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @param {int} v1 a seed value that controls how the device tasks are scheduled
|
||||
* @param {int} count the number of times this task should be scheduled
|
||||
* @constructor
|
||||
*/
|
||||
function IdleTask(scheduler, v1, count) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = v1;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
IdleTask.prototype.run = function (packet) {
|
||||
this.count--;
|
||||
if (this.count == 0) return this.scheduler.holdCurrent();
|
||||
if ((this.v1 & 1) == 0) {
|
||||
this.v1 = this.v1 >> 1;
|
||||
return this.scheduler.release(ID_DEVICE_A);
|
||||
} else {
|
||||
this.v1 = (this.v1 >> 1) ^ 0xD008;
|
||||
return this.scheduler.release(ID_DEVICE_B);
|
||||
}
|
||||
};
|
||||
|
||||
IdleTask.prototype.toString = function () {
|
||||
return "IdleTask"
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that suspends itself after each time it has been run to simulate
|
||||
* waiting for data from an external device.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @constructor
|
||||
*/
|
||||
function DeviceTask(scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = null;
|
||||
}
|
||||
|
||||
DeviceTask.prototype.run = function (packet) {
|
||||
if (packet == null) {
|
||||
if (this.v1 == null) return this.scheduler.suspendCurrent();
|
||||
var v = this.v1;
|
||||
this.v1 = null;
|
||||
return this.scheduler.queue(v);
|
||||
} else {
|
||||
this.v1 = packet;
|
||||
return this.scheduler.holdCurrent();
|
||||
}
|
||||
};
|
||||
|
||||
DeviceTask.prototype.toString = function () {
|
||||
return "DeviceTask";
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that manipulates work packets.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @param {int} v1 a seed used to specify how work packets are manipulated
|
||||
* @param {int} v2 another seed used to specify how work packets are manipulated
|
||||
* @constructor
|
||||
*/
|
||||
function WorkerTask(scheduler, v1, v2) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
}
|
||||
|
||||
WorkerTask.prototype.run = function (packet) {
|
||||
if (packet == null) {
|
||||
return this.scheduler.suspendCurrent();
|
||||
} else {
|
||||
if (this.v1 == ID_HANDLER_A) {
|
||||
this.v1 = ID_HANDLER_B;
|
||||
} else {
|
||||
this.v1 = ID_HANDLER_A;
|
||||
}
|
||||
packet.id = this.v1;
|
||||
packet.a1 = 0;
|
||||
for (var i = 0; i < DATA_SIZE; i++) {
|
||||
this.v2++;
|
||||
if (this.v2 > 26) this.v2 = 1;
|
||||
packet.a2[i] = this.v2;
|
||||
}
|
||||
return this.scheduler.queue(packet);
|
||||
}
|
||||
};
|
||||
|
||||
WorkerTask.prototype.toString = function () {
|
||||
return "WorkerTask";
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that manipulates work packets and then suspends itself.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @constructor
|
||||
*/
|
||||
function HandlerTask(scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = null;
|
||||
this.v2 = null;
|
||||
}
|
||||
|
||||
HandlerTask.prototype.run = function (packet) {
|
||||
if (packet != null) {
|
||||
if (packet.kind == KIND_WORK) {
|
||||
this.v1 = packet.addTo(this.v1);
|
||||
} else {
|
||||
this.v2 = packet.addTo(this.v2);
|
||||
}
|
||||
}
|
||||
if (this.v1 != null) {
|
||||
var count = this.v1.a1;
|
||||
var v;
|
||||
if (count < DATA_SIZE) {
|
||||
if (this.v2 != null) {
|
||||
v = this.v2;
|
||||
this.v2 = this.v2.link;
|
||||
v.a1 = this.v1.a2[count];
|
||||
this.v1.a1 = count + 1;
|
||||
return this.scheduler.queue(v);
|
||||
}
|
||||
} else {
|
||||
v = this.v1;
|
||||
this.v1 = this.v1.link;
|
||||
return this.scheduler.queue(v);
|
||||
}
|
||||
}
|
||||
return this.scheduler.suspendCurrent();
|
||||
};
|
||||
|
||||
HandlerTask.prototype.toString = function () {
|
||||
return "HandlerTask";
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* P a c k e t
|
||||
* --- */
|
||||
|
||||
var DATA_SIZE = 4;
|
||||
|
||||
/**
|
||||
* A simple package of data that is manipulated by the tasks. The exact layout
|
||||
* of the payload data carried by a packet is not importaint, and neither is the
|
||||
* nature of the work performed on packets by the tasks.
|
||||
*
|
||||
* Besides carrying data, packets form linked lists and are hence used both as
|
||||
* data and worklists.
|
||||
* @param {Packet} link the tail of the linked list of packets
|
||||
* @param {int} id an ID for this packet
|
||||
* @param {int} kind the type of this packet
|
||||
* @constructor
|
||||
*/
|
||||
function Packet(link, id, kind) {
|
||||
this.link = link;
|
||||
this.id = id;
|
||||
this.kind = kind;
|
||||
this.a1 = 0;
|
||||
this.a2 = new Array(DATA_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this packet to the end of a worklist, and return the worklist.
|
||||
* @param {Packet} queue the worklist to add this packet to
|
||||
*/
|
||||
Packet.prototype.addTo = function (queue) {
|
||||
this.link = null;
|
||||
if (queue == null) return this;
|
||||
var peek, next = queue;
|
||||
while ((peek = next.link) != null)
|
||||
next = peek;
|
||||
next.link = this;
|
||||
return queue;
|
||||
};
|
||||
|
||||
Packet.prototype.toString = function () {
|
||||
return "Packet";
|
||||
};
|
||||
|
||||
|
||||
runRichards();
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
var n = 40000;
|
||||
var i = 0;
|
||||
var items = [];
|
||||
|
||||
while (i < n) {
|
||||
items.push("digit" + i);
|
||||
i = i + 1;
|
||||
}
|
||||
|
|
@ -46,9 +46,7 @@ class Benchmark {
|
|||
const playerExpectations = getPlayerExpectations(iterations)
|
||||
if (this._players.length != playerExpectations.length)
|
||||
throw "Expect " + playerExpectations.length + ", but actually have " + this._players.length;
|
||||
if (isInBrowser) {
|
||||
for (let playerIdx = 0; playerIdx < playerExpectations.length; playerIdx++)
|
||||
playerExpectations[playerIdx].validate(this._players[playerIdx]);
|
||||
}
|
||||
for (let playerIdx = 0; playerIdx < playerExpectations.length; playerIdx++)
|
||||
playerExpectations[playerIdx].validate(this._players[playerIdx]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,16 +24,8 @@
|
|||
*/
|
||||
"use strict";
|
||||
|
||||
let currentTime;
|
||||
if (this.performance && performance.now)
|
||||
currentTime = function() { return performance.now() };
|
||||
else if (this.preciseTime)
|
||||
currentTime = function() { return preciseTime() * 1000; };
|
||||
else
|
||||
currentTime = function() { return +new Date(); };
|
||||
|
||||
class Benchmark {
|
||||
constructor(verbose = 0)
|
||||
constructor({verbose = 0})
|
||||
{
|
||||
this._verbose = verbose;
|
||||
}
|
||||
|
|
@ -43,9 +35,9 @@ class Benchmark {
|
|||
this.setup();
|
||||
|
||||
for (let iteration = 0; iteration < numIterations; ++iteration) {
|
||||
let before = currentTime();
|
||||
let before = performance.now();
|
||||
this.runOnce();
|
||||
let after = currentTime();
|
||||
let after = performance.now();
|
||||
results.push(after - before);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,12 +57,12 @@ function ShiftRows(s, Nb) { // shift row r of state S left by r bytes [§5.1.
|
|||
function MixColumns(s, Nb) { // combine bytes of each col of state S [§5.1.3]
|
||||
for (var c=0; c<4; c++) {
|
||||
var a = new Array(4); // 'a' is a copy of the current column from 's'
|
||||
var b = new Array(4); // 'b' is a•{02} in GF(2^8)
|
||||
var b = new Array(4); // 'b' is a dot {02} in GF(2^8)
|
||||
for (var i=0; i<4; i++) {
|
||||
a[i] = s[i][c];
|
||||
b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1;
|
||||
}
|
||||
// a[n] ^ b[n] is a•{03} in GF(2^8)
|
||||
// a[n] ^ b[n] is a dot {03} in GF(2^8)
|
||||
s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3
|
||||
s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3
|
||||
s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// Copyright (c) 2019 Paul Miller (https://paulmillr.com)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the “Software”), to deal
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// Copyright (c) 2019 Paul Miller (https://paulmillr.com)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the “Software”), to deal
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// Copyright (c) 2019 Paul Miller (https://paulmillr.com)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the “Software”), to deal
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ function benchmarkImpl(configuration) {
|
|||
|
||||
var simulator = new Simulator(numAircraft);
|
||||
var detector = new CollisionDetector();
|
||||
var lastTime = currentTime();
|
||||
var lastTime = performance.now();
|
||||
var results = [];
|
||||
for (var i = 0; i < numFrames; ++i) {
|
||||
var time = i / 10;
|
||||
|
|
@ -40,7 +40,7 @@ function benchmarkImpl(configuration) {
|
|||
var collisions = detector.handleNewFrame(simulator.simulate(time));
|
||||
|
||||
var before = lastTime;
|
||||
var after = currentTime();
|
||||
var after = performance.now();
|
||||
lastTime = after;
|
||||
var result = {
|
||||
time: after - before,
|
||||
|
|
|
|||
|
|
@ -73,10 +73,3 @@ function averageAbovePercentile(numbers, percentile) {
|
|||
return result;
|
||||
}
|
||||
|
||||
var currentTime;
|
||||
if (this.performance && performance.now)
|
||||
currentTime = function() { return performance.now() };
|
||||
else if (preciseTime)
|
||||
currentTime = function() { return preciseTime() * 1000; };
|
||||
else
|
||||
currentTime = function() { return 0 + new Date(); };
|
||||
|
|
|
|||
87
cli.js
87
cli.js
|
|
@ -23,35 +23,44 @@
|
|||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const isInBrowser = false;
|
||||
console = {
|
||||
log: globalThis?.console?.log ?? print,
|
||||
error: globalThis?.console?.error ?? print,
|
||||
load("./shell-config.js")
|
||||
|
||||
const cliFlags = { __proto__: null };
|
||||
const cliArgs = [];
|
||||
if (globalThis.arguments?.length) {
|
||||
for (const argument of globalThis.arguments)
|
||||
if (argument.startsWith("--")) {
|
||||
const parts = argument.split("=");
|
||||
cliFlags[parts[0].toLowerCase()] = parts.slice(1).join("=");
|
||||
} else
|
||||
cliArgs.push(argument);
|
||||
}
|
||||
|
||||
const isD8 = typeof Realm !== "undefined";
|
||||
if (isD8)
|
||||
globalThis.readFile = read;
|
||||
const isSpiderMonkey = typeof newGlobal !== "undefined";
|
||||
if (isSpiderMonkey) {
|
||||
globalThis.readFile = readRelativeToScript;
|
||||
globalThis.arguments = scriptArgs;
|
||||
function getIntFlag(flags, flag) {
|
||||
if (!(flag in flags))
|
||||
return undefined;
|
||||
const rawValue = flags[flag];
|
||||
const value = parseInt(rawValue);
|
||||
if (value <= 0)
|
||||
throw new Error(`Expected positive value for ${flag}, but got ${rawValue}`);
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof arguments !== "undefined" && arguments.length > 0)
|
||||
testList = arguments.slice();
|
||||
if (typeof testList === "undefined")
|
||||
testList = undefined;
|
||||
|
||||
if (typeof testIterationCount === "undefined")
|
||||
testIterationCount = undefined;
|
||||
|
||||
if ("--iteration-count" in cliFlags)
|
||||
globalThis.testIterationCount = getIntFlag(cliFlags, "--iteration-count");
|
||||
if ("--worst-case-count" in cliFlags)
|
||||
globalThis.testWorstCaseCount = getIntFlag(cliFlags, "--worst-case-count");
|
||||
if ("--dump-json-results" in cliFlags)
|
||||
globalThis.dumpJSONResults = true;
|
||||
if (typeof runMode !== "undefined" && runMode == "RAMification")
|
||||
RAMification = true;
|
||||
else
|
||||
RAMification = false;
|
||||
globalThis.RAMification = true;
|
||||
if ("--ramification" in cliFlags)
|
||||
globalThis.RAMification = true;
|
||||
if ("--no-prefetch" in cliFlags)
|
||||
globalThis.prefetchResources = false;
|
||||
if (cliArgs.length)
|
||||
globalThis.testList = cliArgs;
|
||||
|
||||
load("./JetStreamDriver.js");
|
||||
|
||||
async function runJetStream() {
|
||||
try {
|
||||
|
|
@ -63,4 +72,34 @@ async function runJetStream() {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
runJetStream();
|
||||
|
||||
load("./JetStreamDriver.js");
|
||||
|
||||
if ("--help" in cliFlags) {
|
||||
console.log("JetStream Driver Help");
|
||||
console.log("");
|
||||
|
||||
console.log("Options:");
|
||||
console.log(" --iteration-count: Set the default iteration count.");
|
||||
console.log(" --worst-case-count: Set the default worst-case count");
|
||||
console.log(" --dump-json-results: Print summary json to the console.");
|
||||
console.log(" --dump-test-list: Print test list instead of running.");
|
||||
console.log(" --ramification: Enable ramification support. See RAMification.py for more details.");
|
||||
console.log(" --no-prefetch: Do not prefetch resources. Will add network overhead to measurements!");
|
||||
console.log("");
|
||||
|
||||
console.log("Available tags:");
|
||||
const tagNames = Array.from(benchmarksByTag.keys()).sort();
|
||||
for (const tagName of tagNames)
|
||||
console.log(" ", tagName);
|
||||
console.log("");
|
||||
|
||||
console.log("Available tests:");
|
||||
const benchmarkNames = BENCHMARKS.map(b => b.name).sort();
|
||||
for (const benchmark of benchmarkNames)
|
||||
console.log(" ", benchmark);
|
||||
} else if ("--dump-test-list" in cliFlags) {
|
||||
JetStream.dumpTestList();
|
||||
} else {
|
||||
runJetStream();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
let indirectEval = eval;
|
||||
class Benchmark {
|
||||
async init() {
|
||||
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await getString(inspectorPayloadBlob)}`;
|
||||
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await JetStream.getString(JetStream.preload.inspectorPayloadBlob)}`;
|
||||
|
||||
this.index = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
let indirectEval = eval;
|
||||
class Benchmark {
|
||||
async init() {
|
||||
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await getString(inspectorPayloadBlob)}`;
|
||||
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await JetStream.getString(JetStream.preload.inspectorPayloadBlob)}`;
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,13 @@ class File {
|
|||
set data(dataView) { this._data = dataView; }
|
||||
|
||||
swapByteOrder() {
|
||||
let hash = 0x1a2b3c4d;
|
||||
for (let i = 0; i < Math.floor(this.data.byteLength / 8) * 8; i += 8) {
|
||||
this.data.setFloat64(i, this.data.getFloat64(i, isLittleEndian), !isLittleEndian);
|
||||
const data = this.data.getFloat64(i, isLittleEndian);
|
||||
this.data.setFloat64(i, data, !isLittleEndian);
|
||||
hash ^= data | 0;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,11 +175,16 @@ async function setupDirectory() {
|
|||
}
|
||||
|
||||
class Benchmark {
|
||||
EXPECTED_FILE_COUNT = 666;
|
||||
|
||||
totalFileCount = 0;
|
||||
lastFileHash = undefined;
|
||||
|
||||
async runIteration() {
|
||||
const fs = await setupDirectory();
|
||||
|
||||
for await (let { entry: file } of fs.forEachFileRecursively()) {
|
||||
file.swapByteOrder();
|
||||
this.lastFileHash = file.swapByteOrder();
|
||||
}
|
||||
|
||||
for await (let { name, entry: dir } of fs.forEachDirectoryRecursively()) {
|
||||
|
|
@ -187,5 +196,17 @@ class Benchmark {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for await (let _ of fs.forEachFileRecursively()) {
|
||||
this.totalFileCount++;
|
||||
}
|
||||
}
|
||||
|
||||
validate(iterations) {
|
||||
const expectedFileCount = this.EXPECTED_FILE_COUNT * iterations;
|
||||
if (this.totalFileCount != expectedFileCount)
|
||||
throw new Error(`Invalid total file count ${this.totalFileCount}, expected ${expectedFileCount}.`);
|
||||
if (this.lastFileHash === undefined)
|
||||
throw new Error(`Invalid file hash: ${this.lastFileHash}`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,15 +34,19 @@ function computeIsLittleEndian() {
|
|||
}
|
||||
|
||||
const isLittleEndian = computeIsLittleEndian();
|
||||
let globalCounter = 0;
|
||||
|
||||
function randomFileContents(bytes = ((Math.random() * 128) >>> 0) + 2056) {
|
||||
let result = new ArrayBuffer(bytes);
|
||||
function randomFileContents() {
|
||||
const numBytes = (globalCounter % 128) + 2056;
|
||||
globalCounter++;
|
||||
let result = new ArrayBuffer(numBytes);
|
||||
let view = new Uint8Array(result);
|
||||
for (let i = 0; i < bytes; ++i)
|
||||
view[i] = (Math.random() * 255) >>> 0;
|
||||
for (let i = 0; i < numBytes; ++i)
|
||||
view[i] = (i + globalCounter) % 255;
|
||||
return new DataView(result);
|
||||
}
|
||||
|
||||
|
||||
class File {
|
||||
constructor(dataView, permissions) {
|
||||
this._data = dataView;
|
||||
|
|
@ -53,9 +57,13 @@ class File {
|
|||
set data(dataView) { this._data = dataView; }
|
||||
|
||||
swapByteOrder() {
|
||||
let hash = 0x1a2b3c4d;
|
||||
for (let i = 0; i < Math.floor(this.data.byteLength / 8) * 8; i += 8) {
|
||||
this.data.setFloat64(i, this.data.getFloat64(i, isLittleEndian), !isLittleEndian);
|
||||
const data = this.data.getFloat64(i, isLittleEndian);
|
||||
this.data.setFloat64(i, data, !isLittleEndian);
|
||||
hash ^= data | 0;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,19 +149,22 @@ class Directory {
|
|||
function setupDirectory() {
|
||||
const fs = new Directory;
|
||||
let dirs = [fs];
|
||||
let counter = 0;
|
||||
for (let dir of dirs) {
|
||||
for (let i = 0; i < 8; ++i) {
|
||||
if (dirs.length < 250 && Math.random() >= 0.3) {
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
if (dirs.length < 400 && (counter % 3) <= 1) {
|
||||
dirs.push(dir.addDirectory(`dir-${i}`));
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
for (let dir of dirs) {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
if (Math.random() >= 0.6) {
|
||||
if ((counter % 3) === 0) {
|
||||
dir.addFile(`file-${i}`, new File(randomFileContents()));
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -161,11 +172,16 @@ function setupDirectory() {
|
|||
}
|
||||
|
||||
class Benchmark {
|
||||
EXPECTED_FILE_COUNT = 666;
|
||||
|
||||
totalFileCount = 0;
|
||||
lastFileHash = undefined;
|
||||
|
||||
runIteration() {
|
||||
const fs = setupDirectory();
|
||||
|
||||
for (let { entry: file } of fs.forEachFileRecursively()) {
|
||||
file.swapByteOrder();
|
||||
this.lastFileHash = file.swapByteOrder();
|
||||
}
|
||||
|
||||
for (let { name, entry: dir } of fs.forEachDirectoryRecursively()) {
|
||||
|
|
@ -178,5 +194,17 @@ class Benchmark {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let _ of fs.forEachFileRecursively()) {
|
||||
this.totalFileCount++;
|
||||
}
|
||||
}
|
||||
|
||||
validate(iterations) {
|
||||
const expectedFileCount = this.EXPECTED_FILE_COUNT * iterations;
|
||||
if (this.totalFileCount != expectedFileCount)
|
||||
throw new Error(`Invalid total file count ${this.totalFileCount}, expected ${expectedFileCount}.`);
|
||||
if (this.lastFileHash === undefined)
|
||||
throw new Error(`Invalid file hash: ${this.lastFileHash}`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@
|
|||
<link rel="stylesheet" href="JetStream.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<body class="overflow-scroll">
|
||||
<h1 class="logo">
|
||||
<div id="jetstreams">
|
||||
<a href="index.html" class="logo-image">JetStream 3</a>
|
||||
</div>
|
||||
</h1>
|
||||
<main>
|
||||
<main class="overflow-visible">
|
||||
<article>
|
||||
<h2>In-Depth Analysis</h2>
|
||||
|
||||
|
|
@ -776,6 +776,20 @@
|
|||
Source code: <a href="proxy/vue-benchmark.js">vue-benchmark.js</a>
|
||||
</dd>
|
||||
|
||||
<dt id="dotnet-interp-wasm">dotnet-interp-wasm</dt>
|
||||
<dd>
|
||||
Tests <a href="https://github.com/dotnet/runtime">.NET on WebAssembly</a>. This benchmark tests operations
|
||||
on .NET implementation of String, JSON serialization, specifics of .NET exceptions and computation
|
||||
of a 3D scene using Mono Interpreter. Source code: <a href="wasm/dotnet">.NET</a>.
|
||||
</dd>
|
||||
|
||||
<dt id="dotnet-aot-wasm">dotnet-aot-wasm</dt>
|
||||
<dd>
|
||||
Tests <a href="https://github.com/dotnet/runtime">.NET on WebAssembly</a>. This benchmark tests operations
|
||||
on .NET implementation of String, JSON serialization, specifics of .NET exceptions and computation
|
||||
of a 3D scene using Mono AOT. Source code: <a href="wasm/dotnet">.NET</a>.
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
<p><a href="index.html" class="button">← Return to Tests</a></p>
|
||||
|
|
|
|||
517
package-lock.json
generated
517
package-lock.json
generated
|
|
@ -17,11 +17,12 @@
|
|||
"command-line-usage": "^6.1.3",
|
||||
"es-main": "^1.3.0",
|
||||
"eslint": "^8.38.0",
|
||||
"glob": "^11.0.3",
|
||||
"http-server": "^14.1.1",
|
||||
"jsvu": "^2.5.1",
|
||||
"local-web-server": "^5.4.0",
|
||||
"prettier": "^2.8.3",
|
||||
"selenium-webdriver": "^4.8.0"
|
||||
"selenium-webdriver": "^4.35.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
|
|
@ -607,6 +608,101 @@
|
|||
"deprecated": "Use @eslint/object-schema instead",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/balanced-match": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
|
|
@ -1845,6 +1941,13 @@
|
|||
"integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
|
|
@ -2473,6 +2576,36 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child/node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
|
|
@ -2509,7 +2642,8 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
|
|
@ -2582,21 +2716,24 @@
|
|||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
|
||||
"integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
"foreground-child": "^3.3.1",
|
||||
"jackspeak": "^4.1.1",
|
||||
"minimatch": "^10.0.3",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
|
|
@ -2614,6 +2751,32 @@
|
|||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
|
||||
"integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/global-dirs": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
|
||||
|
|
@ -3041,6 +3204,7 @@
|
|||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
|
|
@ -3290,6 +3454,22 @@
|
|||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
|
||||
"integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
|
@ -3418,6 +3598,28 @@
|
|||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jsvu/node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
|
|
@ -4544,6 +4746,13 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
|
|
@ -4604,6 +4813,43 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
|
||||
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
|
||||
"integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
||||
|
|
@ -5028,6 +5274,28 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/run-async": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
||||
|
|
@ -5108,9 +5376,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/selenium-webdriver": {
|
||||
"version": "4.27.0",
|
||||
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.27.0.tgz",
|
||||
"integrity": "sha512-LkTJrNz5socxpPnWPODQ2bQ65eYx9JK+DQMYNihpTjMCqHwgWGYQnQTCAAche2W3ZP87alA+1zYPvgS8tHNzMQ==",
|
||||
"version": "4.35.0",
|
||||
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.35.0.tgz",
|
||||
"integrity": "sha512-Baaeiuyu7BIIsSYf0SI7Mi55gsNmdI00KM0Hcofw1RnAY+0QEVpdh5yAxueDxgTZS8vcbGZFU0NJ6Qc1riIrLg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5122,14 +5390,15 @@
|
|||
"url": "https://opencollective.com/selenium"
|
||||
}
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bazel/runfiles": "^6.3.1",
|
||||
"jszip": "^3.10.1",
|
||||
"tmp": "^0.2.3",
|
||||
"ws": "^8.18.0"
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.21.0"
|
||||
"node": ">= 20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
|
|
@ -5412,6 +5681,32 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
"name": "string-width",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width/node_modules/ansi-regex": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
|
||||
|
|
@ -5445,6 +5740,20 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs": {
|
||||
"name": "strip-ansi",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-eof": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||
|
|
@ -6098,6 +6407,171 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs": {
|
||||
"name": "wrap-ansi",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-regex": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
|
@ -6116,10 +6590,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
|
|
|||
15
package.json
15
package.json
|
|
@ -16,18 +16,21 @@
|
|||
"license": "SEE LICENSE IN LICENSE",
|
||||
"scripts": {
|
||||
"server": "node tests/server.mjs",
|
||||
"compress": "node utils/compress.mjs",
|
||||
"lint:check": "eslint **/*.{js,mjs,jsx,ts,tsx}",
|
||||
"pretty:check": "prettier --check ./",
|
||||
"format:check": "npm run pretty:check && npm run lint:check",
|
||||
"test:chrome": "node tests/run.mjs --browser chrome",
|
||||
"test:firefox": "node tests/run.mjs --browser firefox",
|
||||
"test:safari": "node tests/run.mjs --browser safari",
|
||||
"test:edge": "node tests/run.mjs --browser edge",
|
||||
"test:chrome": "node tests/run-browser.mjs --browser chrome",
|
||||
"test:firefox": "node tests/run-browser.mjs --browser firefox",
|
||||
"test:safari": "node tests/run-browser.mjs --browser safari",
|
||||
"test:edge": "node tests/run-browser.mjs --browser edge",
|
||||
"test:shell": "npm run test:v8 && npm run test:jsc && npm run test:spidermonkey",
|
||||
"test:v8": "node tests/run-shell.mjs --shell v8",
|
||||
"test:jsc": "node tests/run-shell.mjs --shell jsc",
|
||||
"test:spidermonkey": "node tests/run-shell.mjs --shell spidermonkey"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"@babel/core": "^7.21.3",
|
||||
"@babel/eslint-parser": "^7.21.3",
|
||||
"@babel/plugin-proposal-decorators": "^7.21.0",
|
||||
|
|
@ -35,11 +38,11 @@
|
|||
"command-line-usage": "^6.1.3",
|
||||
"es-main": "^1.3.0",
|
||||
"eslint": "^8.38.0",
|
||||
"glob": "^11.0.3",
|
||||
"http-server": "^14.1.1",
|
||||
"jsvu": "^2.5.1",
|
||||
"local-web-server": "^5.4.0",
|
||||
"prettier": "^2.8.3",
|
||||
"selenium-webdriver": "^4.8.0",
|
||||
"@actions/core": "^1.11.1"
|
||||
"selenium-webdriver": "^4.35.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
polyfills/README
Normal file
3
polyfills/README
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Polyfills shared across different workloads, for browser APIs that are not available in JavaScript engine shells, e.g., `TextDecoder`.
|
||||
To make performance between browser and shell measurements not too dissimilar, these shouldn't account for a major part of the runtime (e.g., ideally only be used during initialization, outside of the main measurement loop).
|
||||
Also, please also add a link to the original sources and licensing information in each subdirectory.
|
||||
286
polyfills/fast-text-encoding/1.0.3/text.js
Normal file
286
polyfills/fast-text-encoding/1.0.3/text.js
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright 2017 Sam Thorogood. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Polyfill for TextEncoder and TextDecoder.
|
||||
*
|
||||
* You probably want `text.min.js`, and not this file directly.
|
||||
*/
|
||||
|
||||
//JSC
|
||||
const global = (0,eval)("this");
|
||||
|
||||
(function(scope) {
|
||||
'use strict';
|
||||
|
||||
// fail early
|
||||
if (scope['TextEncoder'] && scope['TextDecoder']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// used for FastTextDecoder
|
||||
const validUtfLabels = ['utf-8', 'utf8', 'unicode-1-1-utf-8'];
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function FastTextEncoder() {
|
||||
// This does not accept an encoding, and always uses UTF-8:
|
||||
// https://www.w3.org/TR/encoding/#dom-textencoder
|
||||
}
|
||||
|
||||
Object.defineProperty(FastTextEncoder.prototype, 'encoding', {value: 'utf-8'});
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {{stream: boolean}=} options
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
FastTextEncoder.prototype['encode'] = function(string, options={stream: false}) {
|
||||
if (options.stream) {
|
||||
throw new Error(`Failed to encode: the 'stream' option is unsupported.`);
|
||||
}
|
||||
|
||||
let pos = 0;
|
||||
const len = string.length;
|
||||
|
||||
let at = 0; // output position
|
||||
let tlen = Math.max(32, len + (len >>> 1) + 7); // 1.5x size
|
||||
let target = new Uint8Array((tlen >>> 3) << 3); // ... but at 8 byte offset
|
||||
|
||||
while (pos < len) {
|
||||
let value = string.charCodeAt(pos++);
|
||||
if (value >= 0xd800 && value <= 0xdbff) {
|
||||
// high surrogate
|
||||
if (pos < len) {
|
||||
const extra = string.charCodeAt(pos);
|
||||
if ((extra & 0xfc00) === 0xdc00) {
|
||||
++pos;
|
||||
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
|
||||
}
|
||||
}
|
||||
if (value >= 0xd800 && value <= 0xdbff) {
|
||||
continue; // drop lone surrogate
|
||||
}
|
||||
}
|
||||
|
||||
// expand the buffer if we couldn't write 4 bytes
|
||||
if (at + 4 > target.length) {
|
||||
tlen += 8; // minimum extra
|
||||
tlen *= (1.0 + (pos / string.length) * 2); // take 2x the remaining
|
||||
tlen = (tlen >>> 3) << 3; // 8 byte offset
|
||||
|
||||
const update = new Uint8Array(tlen);
|
||||
update.set(target);
|
||||
target = update;
|
||||
}
|
||||
|
||||
if ((value & 0xffffff80) === 0) { // 1-byte
|
||||
target[at++] = value; // ASCII
|
||||
continue;
|
||||
} else if ((value & 0xfffff800) === 0) { // 2-byte
|
||||
target[at++] = ((value >>> 6) & 0x1f) | 0xc0;
|
||||
} else if ((value & 0xffff0000) === 0) { // 3-byte
|
||||
target[at++] = ((value >>> 12) & 0x0f) | 0xe0;
|
||||
target[at++] = ((value >>> 6) & 0x3f) | 0x80;
|
||||
} else if ((value & 0xffe00000) === 0) { // 4-byte
|
||||
target[at++] = ((value >>> 18) & 0x07) | 0xf0;
|
||||
target[at++] = ((value >>> 12) & 0x3f) | 0x80;
|
||||
target[at++] = ((value >>> 6) & 0x3f) | 0x80;
|
||||
} else {
|
||||
continue; // out of range
|
||||
}
|
||||
|
||||
target[at++] = (value & 0x3f) | 0x80;
|
||||
}
|
||||
|
||||
// Use subarray if slice isn't supported (IE11). This will use more memory
|
||||
// because the original array still exists.
|
||||
return target.slice ? target.slice(0, at) : target.subarray(0, at);
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {string=} utfLabel
|
||||
* @param {{fatal: boolean}=} options
|
||||
*/
|
||||
function FastTextDecoder(utfLabel='utf-8', options={fatal: false}) {
|
||||
if (validUtfLabels.indexOf(utfLabel.toLowerCase()) === -1) {
|
||||
throw new RangeError(
|
||||
`Failed to construct 'TextDecoder': The encoding label provided ('${utfLabel}') is invalid.`);
|
||||
}
|
||||
// if (options.fatal) {
|
||||
// throw new Error(`Failed to construct 'TextDecoder': the 'fatal' option is unsupported.`);
|
||||
// }
|
||||
}
|
||||
|
||||
Object.defineProperty(FastTextDecoder.prototype, 'encoding', {value: 'utf-8'});
|
||||
|
||||
Object.defineProperty(FastTextDecoder.prototype, 'fatal', {value: false});
|
||||
|
||||
Object.defineProperty(FastTextDecoder.prototype, 'ignoreBOM', {value: false});
|
||||
|
||||
/**
|
||||
* @param {!Uint8Array} bytes
|
||||
* @return {string}
|
||||
*/
|
||||
function decodeBuffer(bytes) {
|
||||
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Uint8Array} bytes
|
||||
* @return {string}
|
||||
*/
|
||||
function decodeSyncXHR(bytes) {
|
||||
const b = new Blob([bytes], {type: 'text/plain;charset=UTF-8'});
|
||||
const u = URL.createObjectURL(b);
|
||||
|
||||
// This hack will fail in non-Edgium Edge because sync XHRs are disabled (and
|
||||
// possibly in other places), so ensure there's a fallback call.
|
||||
try {
|
||||
const x = new XMLHttpRequest();
|
||||
x.open('GET', u, false);
|
||||
x.send();
|
||||
return x.responseText;
|
||||
} catch (e) {
|
||||
return decodeFallback(bytes);
|
||||
} finally {
|
||||
URL.revokeObjectURL(u);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Uint8Array} bytes
|
||||
* @return {string}
|
||||
*/
|
||||
function decodeFallback(bytes) {
|
||||
let inputIndex = 0;
|
||||
|
||||
// Create a working buffer for UTF-16 code points, but don't generate one
|
||||
// which is too large for small input sizes. UTF-8 to UCS-16 conversion is
|
||||
// going to be at most 1:1, if all code points are ASCII. The other extreme
|
||||
// is 4-byte UTF-8, which results in two UCS-16 points, but this is still 50%
|
||||
// fewer entries in the output.
|
||||
const pendingSize = Math.min(256 * 256, bytes.length + 1);
|
||||
const pending = new Uint16Array(pendingSize);
|
||||
const chunks = [];
|
||||
let pendingIndex = 0;
|
||||
|
||||
for (;;) {
|
||||
const more = inputIndex < bytes.length;
|
||||
|
||||
// If there's no more data or there'd be no room for two UTF-16 values,
|
||||
// create a chunk. This isn't done at the end by simply slicing the data
|
||||
// into equal sized chunks as we might hit a surrogate pair.
|
||||
if (!more || (pendingIndex >= pendingSize - 1)) {
|
||||
// nb. .apply and friends are *really slow*. Low-hanging fruit is to
|
||||
// expand this to literally pass pending[0], pending[1], ... etc, but
|
||||
// the output code expands pretty fast in this case.
|
||||
chunks.push(String.fromCharCode.apply(null, pending.subarray(0, pendingIndex)));
|
||||
|
||||
if (!more) {
|
||||
return chunks.join('');
|
||||
}
|
||||
|
||||
// Move the buffer forward and create another chunk.
|
||||
bytes = bytes.subarray(inputIndex);
|
||||
inputIndex = 0;
|
||||
pendingIndex = 0;
|
||||
}
|
||||
|
||||
// The native TextDecoder will generate "REPLACEMENT CHARACTER" where the
|
||||
// input data is invalid. Here, we blindly parse the data even if it's
|
||||
// wrong: e.g., if a 3-byte sequence doesn't have two valid continuations.
|
||||
|
||||
const byte1 = bytes[inputIndex++];
|
||||
if ((byte1 & 0x80) === 0) { // 1-byte or null
|
||||
pending[pendingIndex++] = byte1;
|
||||
} else if ((byte1 & 0xe0) === 0xc0) { // 2-byte
|
||||
const byte2 = bytes[inputIndex++] & 0x3f;
|
||||
pending[pendingIndex++] = ((byte1 & 0x1f) << 6) | byte2;
|
||||
} else if ((byte1 & 0xf0) === 0xe0) { // 3-byte
|
||||
const byte2 = bytes[inputIndex++] & 0x3f;
|
||||
const byte3 = bytes[inputIndex++] & 0x3f;
|
||||
pending[pendingIndex++] = ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3;
|
||||
} else if ((byte1 & 0xf8) === 0xf0) { // 4-byte
|
||||
const byte2 = bytes[inputIndex++] & 0x3f;
|
||||
const byte3 = bytes[inputIndex++] & 0x3f;
|
||||
const byte4 = bytes[inputIndex++] & 0x3f;
|
||||
|
||||
// this can be > 0xffff, so possibly generate surrogates
|
||||
let codepoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
|
||||
if (codepoint > 0xffff) {
|
||||
// codepoint &= ~0x10000;
|
||||
codepoint -= 0x10000;
|
||||
pending[pendingIndex++] = (codepoint >>> 10) & 0x3ff | 0xd800;
|
||||
codepoint = 0xdc00 | codepoint & 0x3ff;
|
||||
}
|
||||
pending[pendingIndex++] = codepoint;
|
||||
} else {
|
||||
// invalid initial byte
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoding a string is pretty slow, but use alternative options where possible.
|
||||
let decodeImpl = decodeFallback;
|
||||
if (typeof Buffer === 'function' && Buffer.from) {
|
||||
// Buffer.from was added in Node v5.10.0 (2015-11-17).
|
||||
decodeImpl = decodeBuffer;
|
||||
} else if (typeof Blob === 'function' && typeof URL === 'function' && typeof URL.createObjectURL === 'function') {
|
||||
// Blob and URL.createObjectURL are available from IE10, Safari 6, Chrome 19
|
||||
// (all released in 2012), Firefox 19 (2013), ...
|
||||
decodeImpl = decodeSyncXHR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(!ArrayBuffer|!ArrayBufferView)} buffer
|
||||
* @param {{stream: boolean}=} options
|
||||
* @return {string}
|
||||
*/
|
||||
FastTextDecoder.prototype['decode'] = function(buffer, options={stream: false}) {
|
||||
if (options['stream']) {
|
||||
throw new Error(`Failed to decode: the 'stream' option is unsupported.`);
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
let bytes;
|
||||
|
||||
if (buffer instanceof Uint8Array) {
|
||||
// Accept Uint8Array instances as-is.
|
||||
bytes = buffer;
|
||||
} else if (buffer.buffer instanceof ArrayBuffer) {
|
||||
// Look for ArrayBufferView, which isn't a real type, but basically
|
||||
// represents all the valid TypedArray types plus DataView. They all have
|
||||
// ".buffer" as an instance of ArrayBuffer.
|
||||
bytes = new Uint8Array(buffer.buffer);
|
||||
} else {
|
||||
// The only other valid argument here is that "buffer" is an ArrayBuffer.
|
||||
// We also try to convert anything else passed to a Uint8Array, as this
|
||||
// catches anything that's array-like. Native code would throw here.
|
||||
bytes = new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
return decodeImpl(/** @type {!Uint8Array} */ (bytes));
|
||||
}
|
||||
|
||||
scope['TextEncoder'] = FastTextEncoder;
|
||||
scope['TextDecoder'] = FastTextDecoder;
|
||||
|
||||
}(typeof window !== 'undefined' ? window : (typeof global !== 'undefined' ? global : this)));
|
||||
201
polyfills/fast-text-encoding/LICENSE
Normal file
201
polyfills/fast-text-encoding/LICENSE
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
1
polyfills/fast-text-encoding/README
Normal file
1
polyfills/fast-text-encoding/README
Normal file
|
|
@ -0,0 +1 @@
|
|||
See https://github.com/samthor/fast-text-encoding.
|
||||
|
|
@ -23,8 +23,35 @@
|
|||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
class Benchmark {
|
||||
runIteration() {
|
||||
__web_tooling_global_suites__.chai.fn();
|
||||
}
|
||||
const isInBrowser = false;
|
||||
if (typeof console == "undefined")
|
||||
console = {};
|
||||
|
||||
console.debug ??= (...args) => console.log("Debug:", ...args);
|
||||
console.log ??= (...args) => print(args.join(" "));
|
||||
console.warn ??= (...args) => console.log("Warn:", ...args);
|
||||
console.error ??= (...args) => console.log("Error:", ...args);
|
||||
console.assert ??= (condition, message) => {
|
||||
if (!condition)
|
||||
throw new Error(`Assertion failed: ${message}`);
|
||||
};
|
||||
console.trace ??= () => {
|
||||
const targetObject = {};
|
||||
Error.captureStackTrace(targetObject);
|
||||
console.log(targetObject.stack);
|
||||
};
|
||||
|
||||
const isD8 = typeof Realm !== "undefined";
|
||||
if (isD8)
|
||||
globalThis.readFile = read;
|
||||
const isSpiderMonkey = typeof newGlobal !== "undefined";
|
||||
if (isSpiderMonkey) {
|
||||
globalThis.readFile = readRelativeToScript;
|
||||
globalThis.arguments = scriptArgs;
|
||||
}
|
||||
|
||||
if (typeof performance == "undefined")
|
||||
performance = {};
|
||||
|
||||
performance.mark ??= function(){};
|
||||
performance.measure ??= function(){};
|
||||
|
|
@ -11,28 +11,6 @@
|
|||
// Empty `URLSearchParams` has just the same interface as a `Map`.
|
||||
globalThis.URLSearchParams = Map;
|
||||
|
||||
// `TextEncoder` and `TextDecoder`. These are called only a few times with short
|
||||
// ASCII strings, so this is sufficient and not performance-critical.
|
||||
class TextEncoder {
|
||||
encode(string) {
|
||||
return Uint8Array.from(string, (char) => {
|
||||
let byte = char.codePointAt(0);
|
||||
if (byte > 0x7f)
|
||||
throw new Error("TextEncoder polyfill only supports ASCII");
|
||||
return byte;
|
||||
});
|
||||
}
|
||||
}
|
||||
class TextDecoder {
|
||||
decode(array) {
|
||||
for (let byte of array) {
|
||||
if (byte > 0x7f)
|
||||
throw new Error("TextDecoder polyfill only supports ASCII");
|
||||
}
|
||||
return String.fromCharCode.apply(null, array);
|
||||
}
|
||||
}
|
||||
|
||||
// Now, some configuration options for when we initialize SQLite.
|
||||
|
||||
// Use JetStream functions instead of `console.log` and friends.
|
||||
|
|
@ -53,7 +31,7 @@ class Benchmark {
|
|||
sqlite3Module;
|
||||
|
||||
async init() {
|
||||
Module.wasmBinary = await getBinary(wasmBinary);
|
||||
Module.wasmBinary = await JetStream.getBinary(JetStream.preload.wasmBinary);
|
||||
}
|
||||
|
||||
async runIteration() {
|
||||
|
|
|
|||
27
startup-helper/BabelCacheBuster.mjs
Normal file
27
startup-helper/BabelCacheBuster.mjs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Babel plugin that adds CACHE_BUST_COMMENT to every function body.
|
||||
const CACHE_BUST_COMMENT = "ThouShaltNotCache";
|
||||
|
||||
export default function({ types: t }) {
|
||||
return {
|
||||
visitor: {
|
||||
Function(path) {
|
||||
const bodyPath = path.get("body");
|
||||
// Handle arrow functions: () => "value"
|
||||
// Convert them to block statements: () => { return "value"; }
|
||||
if (!bodyPath.isBlockStatement()) {
|
||||
const newBody = t.blockStatement([t.returnStatement(bodyPath.node)]);
|
||||
path.set("body", newBody);
|
||||
}
|
||||
|
||||
// Handle empty function bodies: function foo() {}
|
||||
// Add an empty statement so we have a first node to attach the comment to.
|
||||
if (path.get("body.body").length === 0) {
|
||||
path.get("body").pushContainer("body", t.emptyStatement());
|
||||
}
|
||||
|
||||
const firstNode = path.node.body.body[0];
|
||||
t.addComment(firstNode, "leading", CACHE_BUST_COMMENT);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
128
startup-helper/StartupBenchmark.js
Normal file
128
startup-helper/StartupBenchmark.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
const CACHE_BUST_COMMENT = "/*ThouShaltNotCache*/";
|
||||
const CACHE_BUST_COMMENT_RE = new RegExp(
|
||||
`\n${RegExp.escape(CACHE_BUST_COMMENT)}\n`,
|
||||
"g"
|
||||
);
|
||||
|
||||
class StartupBenchmark {
|
||||
// Total iterations for this benchmark.
|
||||
#iterationCount = 0;
|
||||
// Original source code.
|
||||
#sourceCode;
|
||||
// quickHahs(this.#sourceCode) for use in custom validate() methods.
|
||||
#sourceHash = 0;
|
||||
// Number of no-cache comments in the original #sourceCode.
|
||||
#expectedCacheCommentCount = 0;
|
||||
// How many times (separate iterations) should we reuse the source code.
|
||||
// Use 0 to skip and only use a single #sourceCode string.
|
||||
#sourceCodeReuseCount = 1;
|
||||
// #sourceCode for each iteration, number of unique sources is controlled
|
||||
// by codeReuseCount;
|
||||
#iterationSourceCodes = [];
|
||||
|
||||
constructor({
|
||||
iterationCount,
|
||||
expectedCacheCommentCount,
|
||||
sourceCodeReuseCount = 1,
|
||||
} = {}) {
|
||||
console.assert(
|
||||
iterationCount > 0,
|
||||
`Expected iterationCount to be positive, but got ${iterationCount}`
|
||||
);
|
||||
this.#iterationCount = iterationCount;
|
||||
console.assert(
|
||||
expectedCacheCommentCount > 0,
|
||||
`Expected expectedCacheCommentCount to be positive, but got ${expectedCacheCommentCount}`
|
||||
);
|
||||
this.#expectedCacheCommentCount = expectedCacheCommentCount;
|
||||
console.assert(
|
||||
sourceCodeReuseCount >= 0,
|
||||
`Expected sourceCodeReuseCount to be non-negative, but got ${sourceCodeReuseCount}`
|
||||
);
|
||||
this.#sourceCodeReuseCount = sourceCodeReuseCount;
|
||||
}
|
||||
|
||||
get iterationCount() {
|
||||
return this.#iterationCount;
|
||||
}
|
||||
|
||||
get sourceCode() {
|
||||
return this.#sourceCode;
|
||||
}
|
||||
|
||||
get sourceHash() {
|
||||
return this.#sourceHash;
|
||||
}
|
||||
|
||||
get expectedCacheCommentCount() {
|
||||
return this.#expectedCacheCommentCount;
|
||||
}
|
||||
|
||||
get sourceCodeReuseCount() {
|
||||
return this.#sourceCodeReuseCount;
|
||||
}
|
||||
|
||||
get iterationSourceCodes() {
|
||||
return this.#iterationSourceCodes;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.#sourceCode = await JetStream.getString(JetStream.preload.BUNDLE);
|
||||
const cacheCommentCount = this.sourceCode.match(
|
||||
CACHE_BUST_COMMENT_RE
|
||||
).length;
|
||||
this.#sourceHash = this.quickHash(this.sourceCode);
|
||||
this.validateSourceCacheComments(cacheCommentCount);
|
||||
for (let i = 0; i < this.iterationCount; i++)
|
||||
this.#iterationSourceCodes[i] = this.createIterationSourceCode(i);
|
||||
this.validateIterationSourceCodes();
|
||||
}
|
||||
|
||||
validateSourceCacheComments(cacheCommentCount) {
|
||||
console.assert(
|
||||
cacheCommentCount === this.expectedCacheCommentCount,
|
||||
`Invalid cache comment count ${cacheCommentCount} expected ${this.expectedCacheCommentCount}.`
|
||||
);
|
||||
}
|
||||
|
||||
validateIterationSourceCodes() {
|
||||
if (this.#iterationSourceCodes.some((each) => !each?.length))
|
||||
throw new Error(`Got invalid iterationSourceCodes`);
|
||||
let expectedSize = 1;
|
||||
if (this.sourceCodeReuseCount !== 0)
|
||||
expectedSize = Math.ceil(this.iterationCount / this.sourceCodeReuseCount);
|
||||
const uniqueSources = new Set(this.iterationSourceCodes);
|
||||
if (uniqueSources.size != expectedSize)
|
||||
throw new Error(
|
||||
`Expected ${expectedSize} unique sources, but got ${uniqueSources.size}.`
|
||||
);
|
||||
}
|
||||
|
||||
createIterationSourceCode(iteration) {
|
||||
// Alter the code per iteration to prevent caching.
|
||||
const cacheId =
|
||||
Math.floor(iteration / this.sourceCodeReuseCount) *
|
||||
this.sourceCodeReuseCount;
|
||||
// Reuse existing sources if this.codeReuseCount > 1:
|
||||
if (cacheId < this.iterationSourceCodes.length)
|
||||
return this.iterationSourceCodes[cacheId];
|
||||
|
||||
const sourceCode = this.sourceCode.replaceAll(
|
||||
CACHE_BUST_COMMENT_RE,
|
||||
`/*${cacheId}*/`
|
||||
);
|
||||
// Warm up quickHash.
|
||||
this.quickHash(sourceCode);
|
||||
return sourceCode;
|
||||
}
|
||||
|
||||
quickHash(str) {
|
||||
let hash = 5381;
|
||||
let i = str.length;
|
||||
while (i > 0) {
|
||||
hash = (hash * 33) ^ (str.charCodeAt(i) | 0);
|
||||
i -= 919;
|
||||
}
|
||||
return hash | 0;
|
||||
}
|
||||
}
|
||||
82
tests/helper.mjs
Normal file
82
tests/helper.mjs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { styleText } from "node:util";
|
||||
import core from "@actions/core";
|
||||
import commandLineUsage from "command-line-usage";
|
||||
|
||||
export const GITHUB_ACTIONS_OUTPUT = "GITHUB_ACTIONS_OUTPUT" in process.env;
|
||||
|
||||
export function logInfo(...args) {
|
||||
const text = args.join(" ")
|
||||
if (GITHUB_ACTIONS_OUTPUT)
|
||||
core.info(styleText("yellow", text));
|
||||
else
|
||||
console.log(styleText("yellow", text));
|
||||
}
|
||||
|
||||
export function logError(...args) {
|
||||
let error;
|
||||
if (args.length == 1 && args[0] instanceof Error)
|
||||
error = args[0];
|
||||
const text = args.join(" ");
|
||||
if (GITHUB_ACTIONS_OUTPUT) {
|
||||
if (error?.stack)
|
||||
core.error(error.stack);
|
||||
else
|
||||
core.error(styleText("red", text));
|
||||
} else {
|
||||
if (error?.stack)
|
||||
console.error(styleText("red", error.stack));
|
||||
else
|
||||
console.error(styleText("red", text));
|
||||
}
|
||||
}
|
||||
|
||||
export async function logGroup(name, body) {
|
||||
if (GITHUB_ACTIONS_OUTPUT) {
|
||||
core.startGroup(name);
|
||||
} else {
|
||||
logInfo("=".repeat(80));
|
||||
logInfo(name);
|
||||
logInfo(".".repeat(80));
|
||||
}
|
||||
try {
|
||||
return await body();
|
||||
} finally {
|
||||
if (GITHUB_ACTIONS_OUTPUT)
|
||||
core.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function printHelp(message = "", optionDefinitions) {
|
||||
const usage = commandLineUsage([
|
||||
{
|
||||
header: "Run all tests",
|
||||
},
|
||||
{
|
||||
header: "Options",
|
||||
optionList: optionDefinitions,
|
||||
},
|
||||
]);
|
||||
if (!message) {
|
||||
console.log(usage);
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error(message);
|
||||
console.error();
|
||||
console.error(usage);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function runTest(label, testFunction) {
|
||||
try {
|
||||
await logGroup(label, testFunction);
|
||||
logInfo("✅ Test completed!");
|
||||
} catch(e) {
|
||||
logError("❌ Test failed!");
|
||||
logError(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
165
tests/run-browser.mjs
Normal file
165
tests/run-browser.mjs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
#! /usr/bin/env node
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
import serve from "./server.mjs";
|
||||
import { Builder, Capabilities } from "selenium-webdriver";
|
||||
import commandLineArgs from "command-line-args";
|
||||
|
||||
import {logInfo, logError, printHelp, runTest} from "./helper.mjs";
|
||||
|
||||
const optionDefinitions = [
|
||||
{ name: "browser", type: String, description: "Set the browser to test, choices are [safari, firefox, chrome, edge]. By default the $BROWSER env variable is used." },
|
||||
{ name: "port", type: Number, defaultValue: 8010, description: "Set the test-server port, The default value is 8010." },
|
||||
{ name: "help", alias: "h", description: "Print this help text." },
|
||||
];
|
||||
|
||||
|
||||
const options = commandLineArgs(optionDefinitions);
|
||||
|
||||
if ("help" in options)
|
||||
printHelp(optionDefinitions);
|
||||
|
||||
const BROWSER = options?.browser;
|
||||
if (!BROWSER)
|
||||
printHelp("No browser specified, use $BROWSER or --browser", optionDefinitions);
|
||||
|
||||
let capabilities;
|
||||
switch (BROWSER) {
|
||||
case "safari":
|
||||
capabilities = Capabilities.safari();
|
||||
break;
|
||||
|
||||
case "firefox": {
|
||||
capabilities = Capabilities.firefox();
|
||||
break;
|
||||
}
|
||||
case "chrome": {
|
||||
capabilities = Capabilities.chrome();
|
||||
break;
|
||||
}
|
||||
case "edge": {
|
||||
capabilities = Capabilities.edge();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
printHelp(`Invalid browser "${BROWSER}", choices are: "safari", "firefox", "chrome", "edge"`);
|
||||
}
|
||||
}
|
||||
|
||||
process.on("unhandledRejection", (err) => {
|
||||
logError(err);
|
||||
process.exit(1);
|
||||
});
|
||||
process.once("uncaughtException", (err) => {
|
||||
logError(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const PORT = options.port;
|
||||
const server = await serve(PORT);
|
||||
|
||||
async function runTests() {
|
||||
let success = true;
|
||||
try {
|
||||
success &&= await runEnd2EndTest("Run Single Suite", { test: "proxy-mobx" });
|
||||
success &&= await runEnd2EndTest("Run Tag No Prefetch", { tag: "proxy", prefetchResources: "false" });
|
||||
success &&= await runEnd2EndTest("Run Disabled Suite", { tag: "disabled" });
|
||||
success &&= await runEnd2EndTest("Run Default Suite");
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
if (!success)
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function runEnd2EndTest(name, params) {
|
||||
return runTest(name, () => testEnd2End(params));
|
||||
}
|
||||
|
||||
async function testEnd2End(params) {
|
||||
const driver = await new Builder().withCapabilities(capabilities).build();
|
||||
const driverCapabilities = await driver.getCapabilities();
|
||||
logInfo(`Browser: ${driverCapabilities.getBrowserName()} ${driverCapabilities.getBrowserVersion()}`);
|
||||
const urlParams = Object.assign({
|
||||
worstCaseCount: 2,
|
||||
iterationCount: 3
|
||||
}, params);
|
||||
let results;
|
||||
try {
|
||||
const url = new URL(`http://localhost:${PORT}/index.html`);
|
||||
url.search = new URLSearchParams(urlParams).toString();
|
||||
logInfo(`JetStream PREPARE ${url}`);
|
||||
await driver.get(url.toString());
|
||||
await driver.executeAsyncScript((callback) => {
|
||||
// callback() is explicitly called without the default event
|
||||
// as argument to avoid serialization issues with chromedriver.
|
||||
globalThis.addEventListener("JetStreamReady", () => callback());
|
||||
// We might not get a chance to install the on-ready listener, thus
|
||||
// we also check if the runner is ready synchronously.
|
||||
if (globalThis?.JetStream?.isReady)
|
||||
callback();
|
||||
});
|
||||
results = await benchmarkResults(driver);
|
||||
// FIXME: validate results;
|
||||
} catch(e) {
|
||||
throw e;
|
||||
} finally {
|
||||
driver.quit();
|
||||
}
|
||||
}
|
||||
|
||||
async function benchmarkResults(driver) {
|
||||
logInfo("JetStream START");
|
||||
await driver.manage().setTimeouts({ script: 2 * 60_000 });
|
||||
await driver.executeAsyncScript((callback) => {
|
||||
globalThis.JetStream.start();
|
||||
callback();
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => pollResultsUntilDone(driver, resolve, reject));
|
||||
const resultsJSON = await driver.executeScript(() => {
|
||||
return globalThis.JetStream.resultsJSON();
|
||||
});
|
||||
return JSON.parse(resultsJSON);
|
||||
}
|
||||
|
||||
class JetStreamTestError extends Error {
|
||||
constructor(errors) {
|
||||
super(`Tests failed: ${errors.map(e => e.stack).join(", ")}`);
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const UPDATE_INTERVAL = 250;
|
||||
async function pollResultsUntilDone(driver, resolve, reject) {
|
||||
const previousResults = new Set();
|
||||
const intervalId = setInterval(async function logResult() {
|
||||
const {done, errors, resultsJSON} = await driver.executeScript(() => {
|
||||
return {
|
||||
done: globalThis.JetStream.isDone,
|
||||
errors: globalThis.JetStream.errors,
|
||||
resultsJSON: JSON.stringify(globalThis.JetStream.resultsObject("simple")),
|
||||
};
|
||||
});
|
||||
if (errors.length) {
|
||||
clearInterval(intervalId);
|
||||
reject(new JetStreamTestError(errors));
|
||||
}
|
||||
logIncrementalResult(previousResults, JSON.parse(resultsJSON));
|
||||
if (done) {
|
||||
clearInterval(intervalId);
|
||||
resolve();
|
||||
}
|
||||
}, UPDATE_INTERVAL)
|
||||
}
|
||||
|
||||
function logIncrementalResult(previousResults, benchmarkResults) {
|
||||
for (const [testName, testResults] of Object.entries(benchmarkResults)) {
|
||||
if (previousResults.has(testName))
|
||||
continue;
|
||||
console.log(testName, testResults);
|
||||
previousResults.add(testName);
|
||||
}
|
||||
}
|
||||
|
||||
setImmediate(runTests);
|
||||
|
|
@ -1,49 +1,29 @@
|
|||
#! /usr/bin/env node
|
||||
|
||||
import commandLineArgs from "command-line-args";
|
||||
import commandLineUsage from "command-line-usage";
|
||||
import { spawnSync } from "child_process";
|
||||
import { spawn } from "child_process";
|
||||
import { fileURLToPath } from "url";
|
||||
import { styleText } from "node:util";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import core from "@actions/core"
|
||||
import core from "@actions/core";
|
||||
|
||||
import {logInfo, logError, logGroup, printHelp, runTest, GITHUB_ACTIONS_OUTPUT} from "./helper.mjs";
|
||||
|
||||
const optionDefinitions = [
|
||||
{ name: "shell", type: String, description: "Set the shell to test, choices are [jsc, v8, spidermonkey]." },
|
||||
{ name: "help", alias: "h", description: "Print this help text." },
|
||||
];
|
||||
|
||||
function printHelp(message = "") {
|
||||
const usage = commandLineUsage([
|
||||
{
|
||||
header: "Run all tests",
|
||||
},
|
||||
{
|
||||
header: "Options",
|
||||
optionList: optionDefinitions,
|
||||
},
|
||||
]);
|
||||
if (!message) {
|
||||
console.log(usage);
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error(message);
|
||||
console.error();
|
||||
console.error(usage);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const options = commandLineArgs(optionDefinitions);
|
||||
|
||||
if ("help" in options)
|
||||
printHelp();
|
||||
printHelp(optionDefinitions);
|
||||
|
||||
const JS_SHELL= options?.shell;
|
||||
if (!JS_SHELL)
|
||||
printHelp("No javascript shell specified, use --shell");
|
||||
printHelp("No javascript shell specified, use --shell", optionDefinitions);
|
||||
|
||||
const SHELL_NAME = (function() {
|
||||
switch (JS_SHELL) {
|
||||
|
|
@ -66,51 +46,20 @@ const SHELL_NAME = (function() {
|
|||
const FILE_PATH = fileURLToPath(import.meta.url);
|
||||
const SRC_DIR = path.dirname(path.dirname(FILE_PATH));
|
||||
const CLI_PATH = path.join(SRC_DIR, "cli.js");
|
||||
const UNIT_TEST_PATH = path.join(SRC_DIR, "tests", "unit-tests.js");
|
||||
|
||||
const BASE_CLI_ARGS_WITH_OPTIONS = [CLI_PATH];
|
||||
if (SHELL_NAME != "spidermonkey")
|
||||
BASE_CLI_ARGS_WITH_OPTIONS.push("--");
|
||||
Object.freeze(BASE_CLI_ARGS_WITH_OPTIONS);
|
||||
|
||||
const GITHUB_ACTIONS_OUTPUT = "GITHUB_ACTIONS_OUTPUT" in process.env;
|
||||
|
||||
function log(...args) {
|
||||
const text = args.join(" ")
|
||||
if (GITHUB_ACTIONS_OUTPUT)
|
||||
core.info(styleText("yellow", text))
|
||||
else
|
||||
console.log(styleText("yellow", text))
|
||||
function convertCliArgs(cli, ...cliArgs) {
|
||||
if (SHELL_NAME == "spidermonkey")
|
||||
return [cli, ...cliArgs];
|
||||
return [cli, "--", ...cliArgs];
|
||||
}
|
||||
|
||||
function logError(...args) {
|
||||
const text = args.join(" ")
|
||||
if (GITHUB_ACTIONS_OUTPUT)
|
||||
core.error(styleText("red", text))
|
||||
else
|
||||
console.error(styleText("red", text))
|
||||
}
|
||||
|
||||
function logGroup(name, body) {
|
||||
if (GITHUB_ACTIONS_OUTPUT) {
|
||||
core.startGroup(name);
|
||||
} else {
|
||||
log("=".repeat(80))
|
||||
log(name);
|
||||
log(".".repeat(80))
|
||||
}
|
||||
try {
|
||||
return body();
|
||||
} finally {
|
||||
if (GITHUB_ACTIONS_OUTPUT)
|
||||
core.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
const SPAWN_OPTIONS = {
|
||||
stdio: ["inherit", "inherit", "inherit"]
|
||||
};
|
||||
|
||||
function sh(binary, args) {
|
||||
async function sh(binary, ...args) {
|
||||
const cmd = `${binary} ${args.join(" ")}`;
|
||||
if (GITHUB_ACTIONS_OUTPUT) {
|
||||
core.startGroup(binary);
|
||||
|
|
@ -119,28 +68,52 @@ function sh(binary, args) {
|
|||
console.log(styleText("blue", cmd));
|
||||
}
|
||||
try {
|
||||
const result = spawnSync(binary, args, SPAWN_OPTIONS);
|
||||
const result = await spawnCaptureStdout(binary, args, SPAWN_OPTIONS);
|
||||
if (result.status || result.error) {
|
||||
logError(result.error);
|
||||
throw new Error(`Shell CMD failed: ${binary} ${args.join(" ")}`);
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
if (GITHUB_ACTIONS_OUTPUT)
|
||||
core.endGroup()
|
||||
core.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
const shellBinary = logGroup(`Installing JavaScript Shell: ${SHELL_NAME}`, testSetup);
|
||||
let success = true;
|
||||
success &&= runTest("Run Complete Suite", () => sh(shellBinary, [CLI_PATH]));
|
||||
success &&= runTest("Run Single Suite", () => {
|
||||
const singleTestArgs = [...BASE_CLI_ARGS_WITH_OPTIONS, "proxy-mobx"];
|
||||
sh(shellBinary, singleTestArgs);
|
||||
async function spawnCaptureStdout(binary, args) {
|
||||
const childProcess = spawn(binary, args);
|
||||
childProcess.stdout.pipe(process.stdout);
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess.stdoutString = "";
|
||||
childProcess.stdio[1].on("data", (data) => {
|
||||
childProcess.stdoutString += data.toString();
|
||||
});
|
||||
if (!success) {
|
||||
process.exit(1)
|
||||
}
|
||||
childProcess.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve(childProcess);
|
||||
} else {
|
||||
// Reject the Promise with an Error on failure
|
||||
const error = new Error(`Command failed with exit code ${code}: ${binary} ${args.join(" ")}`);
|
||||
error.process = childProcess;
|
||||
error.stdout = childProcess.stdoutString;
|
||||
error.exitCode = code;
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
childProcess.on('error', reject);
|
||||
})
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
const shellBinary = await logGroup(`Installing JavaScript Shell: ${SHELL_NAME}`, testSetup);
|
||||
let success = true;
|
||||
success &&= await runTest("Run UnitTests", () => sh(shellBinary, UNIT_TEST_PATH));
|
||||
success &&= await runCLITest("Run Single Suite", shellBinary, "proxy-mobx");
|
||||
success &&= await runCLITest("Run Tag No Prefetch", shellBinary, "proxy", "--no-prefetch");
|
||||
success &&= await runCLITest("Run Disabled Suite", shellBinary, "disabled");
|
||||
success &&= await runCLITest("Run Default Suite", shellBinary);
|
||||
if (!success)
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function jsvuOSName() {
|
||||
|
|
@ -159,30 +132,30 @@ function jsvuOSName() {
|
|||
default: throw new Error("Unsupported architecture");
|
||||
}
|
||||
};
|
||||
return `${osName()}${osArch()}`
|
||||
return `${osName()}${osArch()}`;
|
||||
}
|
||||
|
||||
const DEFAULT_JSC_LOCATION = "/System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Helpers/jsc"
|
||||
|
||||
function testSetup() {
|
||||
sh("jsvu", [`--engines=${SHELL_NAME}`, `--os=${jsvuOSName()}`]);
|
||||
async function testSetup() {
|
||||
await sh("jsvu", `--engines=${SHELL_NAME}`, `--os=${jsvuOSName()}`);
|
||||
let shellBinary = path.join(os.homedir(), ".jsvu/bin", SHELL_NAME);
|
||||
if (!fs.existsSync(shellBinary) && SHELL_NAME == "javascriptcore")
|
||||
shellBinary = DEFAULT_JSC_LOCATION
|
||||
shellBinary = DEFAULT_JSC_LOCATION;
|
||||
if (!fs.existsSync(shellBinary))
|
||||
throw new Error(`Could not find shell binary: ${shellBinary}`);
|
||||
log(`Installed JavaScript Shell: ${shellBinary}`);
|
||||
return shellBinary
|
||||
logInfo(`Installed JavaScript Shell: ${shellBinary}`);
|
||||
return shellBinary;
|
||||
}
|
||||
|
||||
function runTest(testName, test) {
|
||||
try {
|
||||
logGroup(testName, test)
|
||||
} catch(e) {
|
||||
logError("TEST FAILED")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
function runCLITest(name, shellBinary, ...args) {
|
||||
return runTest(name, () => runShell(shellBinary, ...convertCliArgs(CLI_PATH, ...args)));
|
||||
}
|
||||
|
||||
async function runShell(shellBinary, ...args) {
|
||||
const result = await sh(shellBinary, ...args);
|
||||
if (result.stdoutString.includes("JetStream3 failed"))
|
||||
throw new Error("test failed")
|
||||
}
|
||||
|
||||
setImmediate(runTests);
|
||||
|
|
|
|||
260
tests/unit-tests.js
Normal file
260
tests/unit-tests.js
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
load("shell-config.js");
|
||||
load("startup-helper/StartupBenchmark.js");
|
||||
load("JetStreamDriver.js");
|
||||
|
||||
function assertTrue(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message || "Assertion failed");
|
||||
}
|
||||
}
|
||||
|
||||
function assertFalse(condition, message) {
|
||||
if (condition) {
|
||||
throw new Error(message || "Assertion failed");
|
||||
}
|
||||
}
|
||||
|
||||
function assertEquals(actual, expected, message) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(message || `Expected ${expected}, but got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertThrows(message, func) {
|
||||
let didThrow = false;
|
||||
try {
|
||||
func();
|
||||
} catch (e) {
|
||||
didThrow = true;
|
||||
}
|
||||
assertTrue(didThrow, message);
|
||||
}
|
||||
|
||||
(function testTagsAreLowerCaseStrings() {
|
||||
for (const benchmark of BENCHMARKS) {
|
||||
benchmark.tags.forEach((tag) => {
|
||||
assertTrue(typeof tag == "string");
|
||||
assertTrue(tag == tag.toLowerCase());
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
(function testTagsAll() {
|
||||
for (const benchmark of BENCHMARKS) {
|
||||
const tags = benchmark.tags;
|
||||
assertTrue(tags instanceof Set);
|
||||
assertTrue(tags.size > 0);
|
||||
assertTrue(tags.has("all"));
|
||||
assertFalse(tags.has("All"));
|
||||
assertTrue(tags.has("default") ^ tags.has("disabled"));
|
||||
}
|
||||
})();
|
||||
|
||||
(function testDriverBenchmarksOrder() {
|
||||
const benchmarks = findBenchmarksByTag("all");
|
||||
const driver = new Driver(benchmarks);
|
||||
assertEquals(driver.benchmarks.length, BENCHMARKS.length);
|
||||
const names = driver.benchmarks
|
||||
.map((b) => b.name.toLowerCase())
|
||||
.sort()
|
||||
.reverse();
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
assertEquals(driver.benchmarks[i].name.toLowerCase(), names[i]);
|
||||
}
|
||||
})();
|
||||
|
||||
(function testEnableByTag() {
|
||||
const driverA = new Driver(findBenchmarksByTag("Default"));
|
||||
const driverB = new Driver(findBenchmarksByTag("default"));
|
||||
assertTrue(driverA.benchmarks.length > 0);
|
||||
assertEquals(driverA.benchmarks.length, driverB.benchmarks.length);
|
||||
const enabledBenchmarkNames = new Set(
|
||||
Array.from(driverA.benchmarks).map((b) => b.name)
|
||||
);
|
||||
for (const benchmark of BENCHMARKS) {
|
||||
if (benchmark.tags.has("default"))
|
||||
assertTrue(enabledBenchmarkNames.has(benchmark.name));
|
||||
}
|
||||
})();
|
||||
|
||||
(function testDriverEnableDuplicateAndSort() {
|
||||
const benchmarks = [
|
||||
...findBenchmarksByTag("wasm"),
|
||||
...findBenchmarksByTag("wasm"),
|
||||
];
|
||||
assertTrue(benchmarks.length > 0);
|
||||
const uniqueBenchmarks = new Set(benchmarks);
|
||||
assertFalse(uniqueBenchmarks.size == benchmarks.length);
|
||||
const driver = new Driver(benchmarks);
|
||||
assertEquals(driver.benchmarks.length, uniqueBenchmarks.size);
|
||||
})();
|
||||
|
||||
(function testBenchmarkSubScores() {
|
||||
for (const benchmark of BENCHMARKS) {
|
||||
const subScores = benchmark.subScores();
|
||||
assertTrue(subScores instanceof Object);
|
||||
assertTrue(Object.keys(subScores).length > 0);
|
||||
for (const [name, value] of Object.entries(subScores)) {
|
||||
assertTrue(typeof name == "string");
|
||||
// "Score" can only be part of allScores().
|
||||
assertFalse(name == "Score");
|
||||
// Without running values should be either null (or 0 for GroupedBenchmark)
|
||||
assertFalse(value);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
(function testBenchmarkAllScores() {
|
||||
for (const benchmark of BENCHMARKS) {
|
||||
const subScores = benchmark.subScores();
|
||||
const allScores = benchmark.allScores();
|
||||
assertTrue("Score" in allScores);
|
||||
// All subScore items are part of allScores.
|
||||
for (const name of Object.keys(subScores)) assertTrue(name in allScores);
|
||||
}
|
||||
})();
|
||||
|
||||
(function checkUtf16Sources() {
|
||||
// Test that only explicitly UTF16-enabled benchmarks can have sources
|
||||
// with non-8-byte characters.
|
||||
const twoByteCharsRegex = /[^\x00-\xFF]/g;
|
||||
const jsFileRegex = /\.(js|mjs)$/;
|
||||
|
||||
function checkFile(benchmarkName, file, type) {
|
||||
if (!jsFileRegex.test(file))
|
||||
return;
|
||||
const content = read(file);
|
||||
const match = content.match(twoByteCharsRegex);
|
||||
if (!match)
|
||||
return;
|
||||
const uniqueMatches = Array.from(new Set(match));
|
||||
const offendingChars = uniqueMatches.map(char => {
|
||||
const hex = char.charCodeAt(0).toString(16).padStart(4, "0");
|
||||
return `\n - \\u${hex}: '${char}'`;
|
||||
}).join("");
|
||||
throw new Error(
|
||||
`Benchmark '${benchmarkName}' has two-byte characters in ${type} '${file}':\n` +
|
||||
` Offending characters: ${offendingChars}`);
|
||||
}
|
||||
|
||||
for (const benchmark of BENCHMARKS) {
|
||||
if (benchmark.allowUtf16)
|
||||
continue;
|
||||
|
||||
for (const file of benchmark.files) {
|
||||
checkFile(benchmark.name, file, "file");
|
||||
}
|
||||
|
||||
if (benchmark.plan.preload) {
|
||||
for (const [name, file] of Object.entries(benchmark.plan.preload)) {
|
||||
checkFile(benchmark.name, file, `preload.${name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
function validateIterationSources(sources) {
|
||||
for (const source of sources) {
|
||||
assertTrue(typeof source == "string");
|
||||
assertFalse(source.includes(CACHE_BUST_COMMENT));
|
||||
}
|
||||
}
|
||||
|
||||
(async function testStartupBenchmark() {
|
||||
try {
|
||||
JetStream.preload = { BUNDLE: "test-bundle.js" };
|
||||
JetStream.getString = (file) => {
|
||||
assertEquals(file, "test-bundle.js");
|
||||
return `function test() {
|
||||
${CACHE_BUST_COMMENT}
|
||||
return 1;
|
||||
}`;
|
||||
};
|
||||
await testStartupBenchmarkInnerTests();
|
||||
} finally {
|
||||
JetStream.preload = undefined;
|
||||
JetStream.getString = undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
async function testStartupBenchmarkInnerTests() {
|
||||
const benchmark = new StartupBenchmark({
|
||||
iterationCount: 12,
|
||||
expectedCacheCommentCount: 1,
|
||||
});
|
||||
assertEquals(benchmark.iterationCount, 12);
|
||||
assertEquals(benchmark.expectedCacheCommentCount, 1);
|
||||
assertEquals(benchmark.iterationSourceCodes.length, 0);
|
||||
assertEquals(benchmark.sourceCode, undefined);
|
||||
assertEquals(benchmark.sourceHash, 0);
|
||||
await benchmark.init();
|
||||
assertEquals(benchmark.sourceHash, 177573);
|
||||
assertEquals(benchmark.sourceCode.length, 68);
|
||||
assertEquals(benchmark.iterationSourceCodes.length, 12);
|
||||
assertEquals(new Set(benchmark.iterationSourceCodes).size, 12);
|
||||
validateIterationSources(benchmark.iterationSourceCodes);
|
||||
|
||||
const noReuseBenchmark = new StartupBenchmark({
|
||||
iterationCount: 12,
|
||||
expectedCacheCommentCount: 1,
|
||||
sourceCodeReuseCount: 0,
|
||||
});
|
||||
assertEquals(noReuseBenchmark.iterationSourceCodes.length, 0);
|
||||
await noReuseBenchmark.init();
|
||||
assertEquals(noReuseBenchmark.iterationSourceCodes.length, 12);
|
||||
assertEquals(new Set(noReuseBenchmark.iterationSourceCodes).size, 1);
|
||||
validateIterationSources(noReuseBenchmark.iterationSourceCodes);
|
||||
|
||||
const reuseBenchmark = new StartupBenchmark({
|
||||
iterationCount: 12,
|
||||
expectedCacheCommentCount: 1,
|
||||
sourceCodeReuseCount: 3,
|
||||
});
|
||||
assertEquals(reuseBenchmark.iterationSourceCodes.length, 0);
|
||||
await reuseBenchmark.init();
|
||||
assertEquals(reuseBenchmark.iterationSourceCodes.length, 12);
|
||||
assertEquals(new Set(reuseBenchmark.iterationSourceCodes).size, 4);
|
||||
validateIterationSources(reuseBenchmark.iterationSourceCodes);
|
||||
|
||||
const reuseBenchmark2 = new StartupBenchmark({
|
||||
iterationCount: 12,
|
||||
expectedCacheCommentCount: 1,
|
||||
sourceCodeReuseCount: 5,
|
||||
});
|
||||
assertEquals(reuseBenchmark2.iterationSourceCodes.length, 0);
|
||||
await reuseBenchmark2.init();
|
||||
assertEquals(reuseBenchmark2.iterationSourceCodes.length, 12);
|
||||
assertEquals(new Set(reuseBenchmark2.iterationSourceCodes).size, 3);
|
||||
validateIterationSources(reuseBenchmark2.iterationSourceCodes);
|
||||
}
|
||||
|
||||
(function testStartupBenchmarkThrow() {
|
||||
assertThrows(
|
||||
"StartupBenchmark constructor should throw with no arguments.",
|
||||
() => new StartupBenchmark()
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
"StartupBenchmark constructor should throw with missing expectedCacheCommentCount.",
|
||||
() => new StartupBenchmark({ iterationCount: 1 })
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
"StartupBenchmark constructor should throw with missing iterationCount.",
|
||||
() => new StartupBenchmark({ expectedCacheCommentCount: 1 })
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
"StartupBenchmark constructor should throw with iterationCount=0.",
|
||||
() => {
|
||||
new StartupBenchmark({ iterationCount: 0, expectedCacheCommentCount: 1 });
|
||||
}
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
"StartupBenchmark constructor should throw with expectedCacheCommentCount=0.",
|
||||
() => {
|
||||
new StartupBenchmark({ iterationCount: 1, expectedCacheCommentCount: 0 });
|
||||
}
|
||||
);
|
||||
})();
|
||||
224
threejs/benchmark.js
Normal file
224
threejs/benchmark.js
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright (C) 2025 Mozilla. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const NUM_PARTICLES = 8000;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x111111);
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
|
||||
const innerHeight = 1080;
|
||||
const innerWidth = 1920;
|
||||
camera.position.z = 500;
|
||||
|
||||
const canvas = {
|
||||
addEventListener() {},
|
||||
style: {},
|
||||
getContext(kind) {
|
||||
return {
|
||||
getExtension(extension) {
|
||||
if (extension == 'EXT_blend_minmax') {
|
||||
return {MIN_EXT: 32775, MAX_EXT: 32776}
|
||||
}
|
||||
if (extension == 'OES_vertex_array_object') {
|
||||
return {
|
||||
createVertexArrayOES() { return 1 },
|
||||
bindVertexArrayOES() {},
|
||||
deleteVertexArrayOES() {},
|
||||
}
|
||||
}
|
||||
},
|
||||
createTexture() {},
|
||||
bindTexture() {},
|
||||
texImage2D() {},
|
||||
texImage3D() {},
|
||||
texParameteri() {},
|
||||
uniform1i() {},
|
||||
uniform1f() {},
|
||||
uniform2f() {},
|
||||
uniform3f() {},
|
||||
uniform4f() {},
|
||||
clearColor() {},
|
||||
clear() {},
|
||||
clearDepth() {},
|
||||
clearStencil() {},
|
||||
enable() {},
|
||||
disable() {},
|
||||
depthFunc() {},
|
||||
depthMask() {},
|
||||
frontFace() {},
|
||||
cullFace() {},
|
||||
getContextAttributes() {},
|
||||
createBuffer() {},
|
||||
createFramebuffer() {},
|
||||
bindBuffer() {},
|
||||
bufferData() {},
|
||||
createProgram() {},
|
||||
attachShader() {},
|
||||
linkProgram() {},
|
||||
useProgram() {},
|
||||
getAttribLocation() {},
|
||||
getUniformLocation() {},
|
||||
createShader() {},
|
||||
shaderSource() {},
|
||||
compileShader() {},
|
||||
getShaderParameter() {},
|
||||
getProgramInfoLog() { return "" },
|
||||
getShaderInfoLog() { return "" },
|
||||
getProgramParameter() {},
|
||||
deleteShader() {},
|
||||
colorMask() {},
|
||||
stencilMask() {},
|
||||
createVertexArray() {},
|
||||
bindVertexArray() {},
|
||||
drawElements() {},
|
||||
lineWidth() {},
|
||||
drawArrays() {},
|
||||
viewport() {},
|
||||
getParameter(param) {
|
||||
if (param == 34930) { return 16 }
|
||||
if (param == 35660) { return 16 }
|
||||
if (param == 3379) { return 8192 }
|
||||
if (param == 36347) { return 1024 }
|
||||
if (param == 36348) { return 32 }
|
||||
if (param == 36349) { return 1024 }
|
||||
if (param == 35661) { return 80 }
|
||||
if (param == 7938) { return "WebGL 2.0" }
|
||||
if (param == 3088) { return [0,0,1024,480] }
|
||||
if (param == 2978) { return [0,0,1024,480] }
|
||||
},
|
||||
MAX_TEXTURE_IMAGE_UNITS: 34930,
|
||||
MAX_VERTEX_TEXTURE_IMAGE_UNITS: 35660,
|
||||
MAX_TEXTURE_SIZE: 3379,
|
||||
MAX_VERTEX_UNIFORM_VECTORS: 36347,
|
||||
MAX_VARYING_VECTORS: 36348,
|
||||
MAX_FRAGMENT_UNIFORM_VECTORS: 36349,
|
||||
MAX_COMBINED_TEXTURE_IMAGE_UNITS: 35661,
|
||||
VERSION: 7938,
|
||||
SCISSOR_BOX: 3088,
|
||||
VIEWPORT: 2978
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
antialias: false,
|
||||
canvas,
|
||||
powerPreference: 'low-power',
|
||||
precision: 'lowp'
|
||||
});
|
||||
renderer.setSize(innerWidth, innerHeight);
|
||||
|
||||
const createGeometryParticle = (size) => {
|
||||
const visibleHeight = 2 * Math.tan(75 * Math.PI/360) * 500;
|
||||
const radius = (size / innerHeight) * visibleHeight / 2;
|
||||
|
||||
// Main circle
|
||||
const geometry = new THREE.CircleGeometry(radius, 32);
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
color: 0xffffff,
|
||||
depthTest: false
|
||||
});
|
||||
const circle = new THREE.Mesh(geometry, material);
|
||||
|
||||
const posArray = geometry.attributes.position.array;
|
||||
const outlineVertices = [];
|
||||
for (let i = 3; i < posArray.length; i += 3) {
|
||||
outlineVertices.push(
|
||||
new THREE.Vector3(
|
||||
posArray[i],
|
||||
posArray[i + 1],
|
||||
posArray[i + 2]
|
||||
)
|
||||
);
|
||||
}
|
||||
const outlineGeometry = new THREE.BufferGeometry().setFromPoints(outlineVertices);
|
||||
const outline = new THREE.LineLoop(
|
||||
outlineGeometry,
|
||||
new THREE.LineBasicMaterial({ color: 0x000000, depthTest: false })
|
||||
);
|
||||
|
||||
const group = new THREE.Group();
|
||||
group.add(circle);
|
||||
group.add(outline);
|
||||
return group;
|
||||
};
|
||||
|
||||
// Initialize particles
|
||||
var initialized = false;
|
||||
const particles = [];
|
||||
|
||||
function initialize() {
|
||||
for(let i = 0; i < NUM_PARTICLES; i++) {
|
||||
const size = 10 + Math.random() * 80;
|
||||
const particle = createGeometryParticle(size);
|
||||
|
||||
// Random initial position
|
||||
const visibleWidth = 2 * Math.tan(75 * Math.PI/360) * 500 * camera.aspect;
|
||||
particle.position.set(
|
||||
THREE.MathUtils.randFloatSpread(visibleWidth),
|
||||
THREE.MathUtils.randFloatSpread(visibleWidth/camera.aspect),
|
||||
0
|
||||
);
|
||||
|
||||
// Velocity storage
|
||||
particle.velocity = new THREE.Vector2(
|
||||
(Math.random() - 0.5) * 8,
|
||||
(Math.random() - 0.5) * 8
|
||||
);
|
||||
|
||||
scene.add(particle);
|
||||
particles.push(particle);
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// Metrics and animation
|
||||
const visibleWidth = 2 * Math.tan(75 * Math.PI/360) * 500 * camera.aspect;
|
||||
const visibleHeight = visibleWidth / camera.aspect;
|
||||
|
||||
function animate() {
|
||||
particles.forEach(particle => {
|
||||
particle.position.x += particle.velocity.x;
|
||||
particle.position.y += particle.velocity.y;
|
||||
|
||||
// Boundary checks
|
||||
if(Math.abs(particle.position.x) > visibleWidth/2)
|
||||
particle.velocity.x *= -1;
|
||||
if(Math.abs(particle.position.y) > visibleHeight/2)
|
||||
particle.velocity.y *= -1;
|
||||
});
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
class Benchmark {
|
||||
runIteration() {
|
||||
if (!initialized) {
|
||||
initialize();
|
||||
}
|
||||
animate();
|
||||
}
|
||||
}
|
||||
31208
threejs/three.js
Normal file
31208
threejs/three.js
Normal file
File diff suppressed because one or more lines are too long
157
utils/compress.mjs
Normal file
157
utils/compress.mjs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import commandLineArgs from 'command-line-args';
|
||||
import commandLineUsage from 'command-line-usage';
|
||||
import { globSync } from 'glob';
|
||||
import zlib from 'zlib';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
function parseCommandLineArgs() {
|
||||
const optionDefinitions = [
|
||||
{ name: 'decompress', alias: 'd', type: Boolean, description: 'Decompress files (default: compress).' },
|
||||
{ name: 'keep', alias: 'k', type: Boolean, description: 'Keep input files after processing (default: delete).' },
|
||||
{ name: 'help', alias: 'h', type: Boolean, description: 'Print this usage guide.' },
|
||||
{ name: 'globs', type: String, multiple: true, defaultOption: true, description: 'Glob patterns of files to process.' },
|
||||
];
|
||||
const options = commandLineArgs(optionDefinitions);
|
||||
|
||||
const isNPM = process.env.npm_config_user_agent !== undefined;
|
||||
const command = isNPM ? 'npm run compress --' : 'node utils/compress.mjs';
|
||||
const usage = commandLineUsage([
|
||||
{
|
||||
header: 'Usage',
|
||||
content: `${command} [options] <glob>...`
|
||||
},
|
||||
{
|
||||
header: 'Options',
|
||||
optionList: optionDefinitions
|
||||
}
|
||||
]);
|
||||
|
||||
if (options.help) {
|
||||
console.log(usage);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (options.globs === undefined) {
|
||||
if (options.decompress) {
|
||||
const defaultGlob = '**/*.z';
|
||||
console.log(`No input glob pattern given, using default: ${defaultGlob}`);
|
||||
options.globs = [defaultGlob];
|
||||
} else {
|
||||
// For compression, require the user to specify explicit input file patterns.
|
||||
console.error('No input glob pattern given.');
|
||||
console.log(usage);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
function calculateCompressionRatio(originalSize, compressedSize) {
|
||||
return (1 - compressedSize / originalSize) * 100;
|
||||
}
|
||||
|
||||
function calculateExpansionRatio(compressedSize, decompressedSize) {
|
||||
return (decompressedSize / compressedSize - 1) * 100;
|
||||
}
|
||||
|
||||
function compress(inputData) {
|
||||
const compressedData = zlib.deflateSync(inputData, { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
|
||||
const originalSize = inputData.length;
|
||||
const compressedSize = compressedData.length;
|
||||
const compressionRatio = calculateCompressionRatio(originalSize, compressedSize);
|
||||
console.log(` Original size: ${String(originalSize).padStart(8)} bytes`);
|
||||
console.log(` Compressed size: ${String(compressedSize).padStart(8)} bytes`);
|
||||
console.log(` Compression ratio: ${compressionRatio.toFixed(2).padStart(8)}%`);
|
||||
|
||||
return compressedData;
|
||||
}
|
||||
|
||||
function decompress(inputData) {
|
||||
const decompressedData = zlib.inflateSync(inputData);
|
||||
|
||||
const compressedSize = inputData.length;
|
||||
const decompressedSize = decompressedData.length;
|
||||
const expansionRatio = calculateExpansionRatio(compressedSize, decompressedSize);
|
||||
console.log(` Compressed size: ${String(compressedSize).padStart(8)} bytes`);
|
||||
console.log(` Decompressed size: ${String(decompressedSize).padStart(8)} bytes`);
|
||||
console.log(` Expansion ratio: ${expansionRatio.toFixed(2).padStart(8)}%`);
|
||||
|
||||
return decompressedData;
|
||||
}
|
||||
|
||||
function globsToFiles(globs) {
|
||||
let files = [];
|
||||
console.assert(globs.length > 0);
|
||||
for (const glob of globs) {
|
||||
const matches = globSync(glob, { nodir: true });
|
||||
files = files.concat(matches);
|
||||
}
|
||||
files = Array.from(new Set(files)).sort();
|
||||
return files;
|
||||
}
|
||||
|
||||
function processFiles(files, isDecompress, keep) {
|
||||
const verb = isDecompress ? 'decompress' : 'compress';
|
||||
console.log(`Found ${files.length} files to ${verb}` + (files.length ? ':' : '.'));
|
||||
|
||||
// For printing overall statistics at the end.
|
||||
let totalInputSize = 0;
|
||||
let totalOutputSize = 0;
|
||||
|
||||
for (const inputFilename of files) {
|
||||
try {
|
||||
console.log(inputFilename);
|
||||
let outputFilename;
|
||||
if (isDecompress) {
|
||||
if (path.extname(inputFilename) !== '.z') {
|
||||
console.warn(` Warning: Input file does not have a .z extension.`);
|
||||
outputFilename = `${inputFilename}.decompressed`;
|
||||
} else {
|
||||
outputFilename = inputFilename.slice(0, -2);
|
||||
}
|
||||
console.log(` Decompressing to: ${outputFilename}`);
|
||||
} else {
|
||||
if (path.extname(inputFilename) === '.z') {
|
||||
console.warn(` Warning: Input file already has a .z extension.`);
|
||||
}
|
||||
outputFilename = `${inputFilename}.z`;
|
||||
}
|
||||
|
||||
// Copy the mode over to avoid git status entries after a roundtrip.
|
||||
const { mode } = fs.statSync(inputFilename);
|
||||
const inputData = fs.readFileSync(inputFilename);
|
||||
const outputData = isDecompress ? decompress(inputData) : compress(inputData);
|
||||
fs.writeFileSync(outputFilename, outputData, { mode });
|
||||
|
||||
totalInputSize += inputData.length;
|
||||
totalOutputSize += outputData.length;
|
||||
|
||||
if (!keep) {
|
||||
fs.unlinkSync(inputFilename);
|
||||
console.log(` Deleted input file.`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error ${verb}ing ${inputFilename}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (files.length > 1) {
|
||||
if (isDecompress) {
|
||||
const totalExpansionRatio = calculateExpansionRatio(totalInputSize, totalOutputSize);
|
||||
console.log(`Total compressed sizes: ${String(totalInputSize).padStart(9)} bytes`);
|
||||
console.log(`Total decompressed sizes: ${String(totalOutputSize).padStart(9)} bytes`);
|
||||
console.log(`Average expansion ratio: ${totalExpansionRatio.toFixed(2).padStart(9)}%`);
|
||||
} else {
|
||||
const totalCompressionRatio = calculateCompressionRatio(totalInputSize, totalOutputSize);
|
||||
console.log(`Total original sizes: ${String(totalInputSize).padStart(9)} bytes`);
|
||||
console.log(`Total compressed sizes: ${String(totalOutputSize).padStart(9)} bytes`);
|
||||
console.log(`Average compression ratio: ${totalCompressionRatio.toFixed(2).padStart(9)}%`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const options = parseCommandLineArgs();
|
||||
const files = globsToFiles(options.globs);
|
||||
processFiles(files, options.decompress, options.keep);
|
||||
42
wasm-cli.js
42
wasm-cli.js
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
testList = [
|
||||
"HashSet-wasm",
|
||||
"tsf-wasm",
|
||||
"quicksort-wasm",
|
||||
"gcc-loops-wasm",
|
||||
"richards-wasm",
|
||||
"sqlite3-wasm",
|
||||
"tfjs-wasm",
|
||||
"tfjs-wasm-simd",
|
||||
"argon2-wasm",
|
||||
"8bitbench-wasm",
|
||||
"Dart-flute-wasm",
|
||||
"zlib-wasm",
|
||||
];
|
||||
|
||||
// Reuse the full CLI runner, just with the subset of Wasm line items above.
|
||||
load("./cli.js");
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
class Benchmark {
|
||||
async init() {
|
||||
Module.wasmBinary = await getBinary(wasmBinary);
|
||||
Module.wasmBinary = await JetStream.getBinary(JetStream.preload.wasmBinary);
|
||||
}
|
||||
|
||||
async runIteration() {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
class Benchmark {
|
||||
async init() {
|
||||
Module.wasmBinary = await getBinary(wasmBinary);
|
||||
Module.wasmBinary = await JetStream.getBinary(JetStream.preload.wasmBinary);
|
||||
}
|
||||
|
||||
async runIteration() {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ const saltLength = 12;
|
|||
|
||||
class Benchmark {
|
||||
async init() {
|
||||
Module.wasmBinary = await getBinary(wasmBinary);
|
||||
Module.wasmBinary = await JetStream.getBinary(JetStream.preload.wasmBinary);
|
||||
}
|
||||
|
||||
async runIteration() {
|
||||
|
|
|
|||
8
wasm/dotnet/.gitignore
vendored
Normal file
8
wasm/dotnet/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
*.binlog
|
||||
.dotnet
|
||||
.nuget
|
||||
bin
|
||||
obj
|
||||
blazor.boot.json
|
||||
web.config
|
||||
*.staticwebassets.endpoints.json
|
||||
30
wasm/dotnet/README.md
Normal file
30
wasm/dotnet/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# .NET on WebAssembly
|
||||
|
||||
Tests [.NET on WebAssembly](https://github.com/dotnet/runtime). This benchmark tests operations
|
||||
on .NET implementation of String, JSON serialization, specifics of .NET exceptions and computation
|
||||
of a 3D scene using Mono Interpreter & AOT. Source code: [.NET](wasm/dotnet)
|
||||
|
||||
## Build instructions
|
||||
|
||||
Download .NET SDK 9.0.3xx
|
||||
|
||||
- [dotnet-sdk-win-x64.zip](https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x64.zip)
|
||||
- [dotnet-sdk-linux-x64.tar.gz](https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-x64.tar.gz)
|
||||
|
||||
Run `build.sh` script. It will install `wasm-tools` workload & build the benchmark code twice (for Mono interpreter & AOT).
|
||||
|
||||
To run the benchmark code on `jsc`, we are prepending `import.meta.url ??= ""` to `dotnet.js`.
|
||||
|
||||
## Background on .NET / build output files
|
||||
|
||||
Mono AOT works in a "mixed mode". It is not able to compile all code patterns and in various scenarios it falls back to interpreter.
|
||||
Because of that we are still loading managed dlls (all the other not-`dotnet.native.wasm` files).
|
||||
|
||||
Structure of the build output
|
||||
|
||||
- `dotnet.js` is entrypoint JavaScript with public API.
|
||||
- `dotnet.runtime.js` is internal implementation of JavaScript logic for .NET.
|
||||
- `dotnet.native.js` is emscripten module configured for .NET.
|
||||
- `dotnet.native.wasm` is unmanaged code (Mono runtime + AOT compiled code).
|
||||
- `System.*.wasm` is .NET BCL that has unused code trimmed away.
|
||||
- `dotnet.wasm` is the benchmark code.
|
||||
1
wasm/dotnet/aot.js
Normal file
1
wasm/dotnet/aot.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
globalThis.dotnetFlavor = "aot";
|
||||
138
wasm/dotnet/benchmark.js
Normal file
138
wasm/dotnet/benchmark.js
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
class Benchmark {
|
||||
async init() {
|
||||
const config = {
|
||||
mainAssemblyName: "dotnet.dll",
|
||||
globalizationMode: "custom",
|
||||
assets: [
|
||||
{
|
||||
name: "dotnet.runtime.js",
|
||||
resolvedUrl: JetStream.preload.dotnetRuntimeUrl,
|
||||
moduleExports: await JetStream.dynamicImport(JetStream.preload.dotnetRuntimeUrl),
|
||||
behavior: "js-module-runtime"
|
||||
},
|
||||
{
|
||||
name: "dotnet.native.js",
|
||||
resolvedUrl: JetStream.preload.dotnetNativeUrl,
|
||||
moduleExports: await JetStream.dynamicImport(JetStream.preload.dotnetNativeUrl),
|
||||
behavior: "js-module-native"
|
||||
},
|
||||
{
|
||||
name: "dotnet.native.wasm",
|
||||
resolvedUrl: JetStream.preload.wasmBinaryUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.wasmBinaryUrl),
|
||||
behavior: "dotnetwasm"
|
||||
},
|
||||
{
|
||||
name: "icudt_CJK.dat",
|
||||
resolvedUrl: JetStream.preload.icuCustomUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.icuCustomUrl),
|
||||
behavior: "icu"
|
||||
},
|
||||
{
|
||||
name: "System.Collections.Concurrent.wasm",
|
||||
resolvedUrl: JetStream.preload.dllCollectionsConcurrentUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllCollectionsConcurrentUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.Collections.wasm",
|
||||
resolvedUrl: JetStream.preload.dllCollectionsUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllCollectionsUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.ComponentModel.Primitives.wasm",
|
||||
resolvedUrl: JetStream.preload.dllComponentModelPrimitivesUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllComponentModelPrimitivesUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.ComponentModel.TypeConverter.wasm",
|
||||
resolvedUrl: JetStream.preload.dllComponentModelTypeConverterUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllComponentModelTypeConverterUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.Drawing.Primitives.wasm",
|
||||
resolvedUrl: JetStream.preload.dllDrawingPrimitivesUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllDrawingPrimitivesUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.Drawing.wasm",
|
||||
resolvedUrl: JetStream.preload.dllDrawingUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllDrawingUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.IO.Pipelines.wasm",
|
||||
resolvedUrl: JetStream.preload.dllIOPipelinesUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllIOPipelinesUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.Linq.wasm",
|
||||
resolvedUrl: JetStream.preload.dllLinqUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllLinqUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.Memory.wasm",
|
||||
resolvedUrl: JetStream.preload.dllMemoryUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllMemoryUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.ObjectModel.wasm",
|
||||
resolvedUrl: JetStream.preload.dllObjectModelUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllObjectModelUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.Private.CoreLib.wasm",
|
||||
resolvedUrl: JetStream.preload.dllPrivateCorelibUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllPrivateCorelibUrl),
|
||||
behavior: "assembly",
|
||||
isCore: true
|
||||
},
|
||||
{
|
||||
name: "System.Runtime.InteropServices.JavaScript.wasm",
|
||||
resolvedUrl: JetStream.preload.dllRuntimeInteropServicesJavaScriptUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllRuntimeInteropServicesJavaScriptUrl),
|
||||
behavior: "assembly",
|
||||
isCore: true
|
||||
},
|
||||
{
|
||||
name: "System.Text.Encodings.Web.wasm",
|
||||
resolvedUrl: JetStream.preload.dllTextEncodingsWebUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllTextEncodingsWebUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "System.Text.Json.wasm",
|
||||
resolvedUrl: JetStream.preload.dllTextJsonUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllTextJsonUrl),
|
||||
behavior: "assembly"
|
||||
},
|
||||
{
|
||||
name: "dotnet.wasm",
|
||||
resolvedUrl: JetStream.preload.dllAppUrl,
|
||||
buffer: await JetStream.getBinary(JetStream.preload.dllAppUrl),
|
||||
behavior: "assembly",
|
||||
isCore: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
this.dotnet = (await JetStream.dynamicImport(JetStream.preload.dotnetUrl)).dotnet;
|
||||
this.api = await this.dotnet.withModuleConfig({ locateFile: e => e }).withConfig(config).create();
|
||||
this.exports = await this.api.getAssemblyExports("dotnet.dll");
|
||||
|
||||
this.hardwareConcurrency = 1;
|
||||
this.sceneWidth = dotnetFlavor === "aot" ? 300 : 150;
|
||||
this.sceneHeight = dotnetFlavor === "aot" ? 200 : 100;
|
||||
}
|
||||
async runIteration() {
|
||||
await this.exports.Interop.RunIteration(this.sceneWidth, this.sceneHeight, this.hardwareConcurrency);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Collections.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Collections.wasm
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Drawing.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Drawing.wasm
Normal file
Binary file not shown.
Binary file not shown.
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Linq.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Linq.wasm
Normal file
Binary file not shown.
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Memory.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Memory.wasm
Normal file
Binary file not shown.
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.ObjectModel.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.ObjectModel.wasm
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Text.Json.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/System.Text.Json.wasm
Normal file
Binary file not shown.
5
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.js
Normal file
5
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.js
Normal file
File diff suppressed because one or more lines are too long
16
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.native.js
Normal file
16
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.native.js
Normal file
File diff suppressed because one or more lines are too long
26707
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.native.js.symbols
Normal file
26707
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.native.js.symbols
Normal file
File diff suppressed because it is too large
Load diff
BIN
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.native.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.native.wasm
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.wasm
Normal file
BIN
wasm/dotnet/build-aot/wwwroot/_framework/dotnet.wasm
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue