From cb53112caaa20b6955d50229e34ed92a1acc55a8 Mon Sep 17 00:00:00 2001 From: Keith Miller Date: Wed, 27 Nov 2024 13:18:59 -0500 Subject: [PATCH 1/4] WIP Move existing benchmarks to WasmLegacyBenchamrk and add a new WasmBenchmark that supports running iterations. It's currently an async benchmark subclass since the compile is async. As a proof of concept this patch converts tsf-wasm to use the new scoring. As part of this process tsf needed to be recompiled so I added a step to the build.sh script which wraps emcc's output .js file with some glue code. This should make it easier to rebuild in the future too since we're not directly editing the module loading file. I went with this route rather than hooking into emcc's API since I wanted to use the js glue as close to "out of the box" as possible. Lastly, get emcc to ouput the .symbols object so we know which wasm function is which index. We could emit a name section into he binary but my assumption is that most production code does not ship with that so it could change the benchmark in an unrealistic way. TODO: * Profile perf differences * Generalize the wrapping process * Remove postIterationHook code for a different PR. --- JetStreamDriver.js | 143 +- wasm/TSF/build.sh | 25 +- wasm/TSF/tsf.js | 3636 +++++++++++++++++++++++++++++++++++++++ wasm/TSF/tsf.js.symbols | 260 +++ wasm/TSF/tsf.wasm | Bin 0 -> 144449 bytes wasm/TSF/tsf_ir_speed.c | 70 +- 6 files changed, 4091 insertions(+), 43 deletions(-) create mode 100644 wasm/TSF/tsf.js create mode 100644 wasm/TSF/tsf.js.symbols create mode 100755 wasm/TSF/tsf.wasm diff --git a/JetStreamDriver.js b/JetStreamDriver.js index e6995d1..698cf5a 100644 --- a/JetStreamDriver.js +++ b/JetStreamDriver.js @@ -1,7 +1,7 @@ "use strict"; /* - * Copyright (C) 2018-2022 Apple Inc. All rights reserved. + * Copyright (C) 2018-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -330,7 +330,7 @@ class Driver { } else globalObject = runString(""); - globalObject.console = { log: globalObject.print, warn: (e) => { print("Warn: " + e); /*$vm.abort();*/ } } + globalObject.console = { log: globalObject.print, warn: (e) => { print("Warn: " + e); /*$vm.abort();*/ }, error: (e) => { print("Error: " + e); /*$vm.abort();*/ } } globalObject.self = globalObject; globalObject.top = { currentResolve, @@ -543,10 +543,11 @@ class Benchmark { if (__benchmark.prepareForNextIteration) __benchmark.prepareForNextIteration(); - ${this.preiterationCode} + ${this.preIterationCode} let start = performance.now(); __benchmark.runIteration(); let end = performance.now(); + ${this.postIterationCode} results.push(Math.max(1, end - start)); } @@ -565,11 +566,24 @@ class Benchmark { get prerunCode() { return null; } - get preiterationCode() { + get preIterationCode() { + let code = ""; if (this.plan.deterministicRandom) - return `Math.random.__resetSeed();`; + code += `Math.random.__resetSeed();`; - return ""; + if (globalThis.customPreIterationCode) + code += customPreIterationCode; + + return code; + } + + get postIterationCode() { + let code = ""; + + if (globalThis.customPostIterationCode) + code += customPostIterationCode; + + return code; } async run() { @@ -972,10 +986,11 @@ class AsyncBenchmark extends DefaultBenchmark { let __benchmark = new Benchmark(); let results = []; for (let i = 0; i < ${this.iterations}; i++) { - ${this.preiterationCode} + ${this.preIterationCode} let start = performance.now(); await __benchmark.runIteration(); let end = performance.now(); + ${this.postIterationCode} results.push(Math.max(1, end - start)); } if (__benchmark.validate) @@ -986,6 +1001,96 @@ class AsyncBenchmark extends DefaultBenchmark { } }; +class WasmBenchmark extends AsyncBenchmark { + get prerunCode() { + let str = ` + let verbose = true; + + let globalObject = this; + + abort = quit = function() { + if (verbose) + console.log('Intercepted quit/abort'); + }; + + oldPrint = globalObject.print; + globalObject.print = globalObject.printErr = (...args) => { + if (verbose) + console.log('Intercepted print: ', ...args); + }; + + let instanceReady; + let instancePromise = new Promise((resolve) => { + instanceReady = resolve; + }); + + let Module = { + preRun: [], + postRun: [], + print: function() { }, + printErr: function() { }, + setStatus: function(text) { + }, + totalDependencies: 0, + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); + }, + }; + globalObject.Module = Module; + `; + return str; + } + + get runnerCode() { + let str = `function loadBlob(key, path, andThen) {`; + + if (isInBrowser) { + str += ` + var xhr = new XMLHttpRequest(); + xhr.open('GET', path, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = function() { + Module[key] = new Int8Array(xhr.response); + andThen(); + }; + xhr.send(null); + `; + } else { + str += ` + Module[key] = new Int8Array(read(path, "binary")); + + Module.setStatus = null; + Module.monitorRunDependencies = null; + + Promise.resolve(42).then(() => { + try { + andThen(); + } catch(e) { + console.log("error running wasm:", e); + console.log(e.stack); + throw e; + } + }) + `; + } + + str += "}"; + + let keys = Object.keys(this.plan.preload); + for (let i = 0; i < keys.length; ++i) { + str += `loadBlob("${keys[i]}", "${this.plan.preload[keys[i]]}", () => {\n`; + } + str += super.runnerCode; + for (let i = 0; i < keys.length; ++i) { + str += `})`; + } + str += `;`; + + return str; + } +}; + class WSLBenchmark extends Benchmark { constructor(...args) { super(...args); @@ -1062,7 +1167,7 @@ class WSLBenchmark extends Benchmark { } }; -class WasmBenchmark extends Benchmark { +class WasmLegacyBenchmark extends Benchmark { constructor(...args) { super(...args); @@ -1776,16 +1881,16 @@ const testPlans = [ preload: { wasmBinary: "./wasm/HashSet.wasm" }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup }, { name: "tsf-wasm", files: [ - "./wasm/tsf.js" + "./wasm/TSF/tsf.js" ], preload: { - wasmBinary: "./wasm/tsf.wasm" + wasmBinary: "./wasm/TSF/tsf.wasm" }, benchmarkClass: WasmBenchmark, testGroup: WasmGroup @@ -1798,7 +1903,7 @@ const testPlans = [ preload: { wasmBinary: "./wasm/quicksort.wasm" }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup }, { @@ -1809,7 +1914,7 @@ const testPlans = [ preload: { wasmBinary: "./wasm/gcc-loops.wasm" }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup }, { @@ -1820,7 +1925,7 @@ const testPlans = [ preload: { wasmBinary: "./wasm/richards.wasm" }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup }, { @@ -1852,7 +1957,7 @@ const testPlans = [ preload: { tfjsBackendWasmBlob: "./wasm/tfjs-backend-wasm.wasm", }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, async: true, deterministicRandom: true, testGroup: WasmGroup @@ -1873,7 +1978,7 @@ const testPlans = [ preload: { tfjsBackendWasmSimdBlob: "./wasm/tfjs-backend-wasm-simd.wasm", }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, async: true, deterministicRandom: true, testGroup: WasmGroup @@ -1888,7 +1993,7 @@ const testPlans = [ preload: { argon2WasmBlob: "./wasm/argon2.wasm", }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup }, { @@ -1901,7 +2006,7 @@ const testPlans = [ preload: { argon2WasmSimdBlob: "./wasm/argon2-simd.wasm", }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup }, // WorkerTests @@ -1975,7 +2080,7 @@ const testPlans = [ romBinary: "./8bitbench/assets/program.bin" }, async: true, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup } ]; diff --git a/wasm/TSF/build.sh b/wasm/TSF/build.sh index 45017b1..ef51acf 100755 --- a/wasm/TSF/build.sh +++ b/wasm/TSF/build.sh @@ -1,6 +1,9 @@ #!/bin/sh + +set -euo pipefail + emcc \ - -o tsf.html -o tsf.js -O2 -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 \ + -o tsf.js -O2 -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 --emit-symbol-map -s EXPORTED_FUNCTIONS=_main,_runIteration \ -I. -DTSF_BUILD_SYSTEM=1 \ tsf_asprintf.c\ tsf_buffer.c\ @@ -53,3 +56,23 @@ emcc \ tsf_ir_different.c\ tsf_ir_speed.c +TEMPFILE=`mktemp /tmp/tsf.XXXXXX` + +(echo 'function setup(Module) {'; cat tsf.js; echo '} + +class Benchmark { + async runIteration() { + if (!Module["_main"]) { + let runtimeInitializedCallback; + let runtimeInitialized = new Promise((success) => runtimeInitializedCallback = success); + Module.onRuntimeInitialized = function() { + runtimeInitializedCallback(); + } + setup(Module); + await runtimeInitialized; + } + + Module["_runIteration"](); + } +}') > $TEMPFILE +mv $TEMPFILE tsf.js diff --git a/wasm/TSF/tsf.js b/wasm/TSF/tsf.js new file mode 100644 index 0000000..b463a59 --- /dev/null +++ b/wasm/TSF/tsf.js @@ -0,0 +1,3636 @@ +function setup(Module) { +// include: shell.js +// The Module object: Our interface to the outside world. We import +// and export values on it. There are various ways Module can be used: +// 1. Not defined. We create it here +// 2. A function parameter, function(moduleArg) => Promise +// 3. pre-run appended it, var Module = {}; ..generated code.. +// 4. External script tag defines var Module. +// We need to check if Module already exists (e.g. case 3 above). +// Substitution will be replaced with actual code on later stage of the build, +// this way Closure Compiler will not mangle it (e.g. case 4. above). +// Note that if you want to run closure, and also to use Module +// after the generated code, you will need to define var Module = {}; +// before the code. Then that object will be used in the code, and you +// can continue to use Module afterwards as well. +var Module = typeof Module != "undefined" ? Module : {}; + +// Determine the runtime environment we are in. You can customize this by +// setting the ENVIRONMENT setting at compile time (see settings.js). +// Attempt to auto-detect the environment +var ENVIRONMENT_IS_WEB = typeof window == "object"; + +var ENVIRONMENT_IS_WORKER = typeof importScripts == "function"; + +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +var ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string" && process.type != "renderer"; + +if (ENVIRONMENT_IS_NODE) {} + +// --pre-jses are emitted after the Module integration code, so that they can +// refer to Module (if they choose; they can also define Module) +// Sometimes an existing Module object exists with properties +// meant to overwrite the default module functionality. Here +// we collect those properties and reapply _after_ we configure +// the current environment's defaults to avoid having to be so +// defensive during initialization. +var moduleOverrides = Object.assign({}, Module); + +var arguments_ = []; + +var thisProgram = "./this.program"; + +var quit_ = (status, toThrow) => { + throw toThrow; +}; + +// `/` should be present at the end if `scriptDirectory` is not empty +var scriptDirectory = ""; + +function locateFile(path) { + if (Module["locateFile"]) { + return Module["locateFile"](path, scriptDirectory); + } + return scriptDirectory + path; +} + +// Hooks that are implemented differently in different runtime environments. +var readAsync, readBinary; + +if (ENVIRONMENT_IS_NODE) { + // These modules will usually be used on Node.js. Load them eagerly to avoid + // the complexity of lazy-loading. + var fs = require("fs"); + var nodePath = require("path"); + scriptDirectory = __dirname + "/"; + // include: node_shell_read.js + readBinary = filename => { + // We need to re-wrap `file://` strings to URLs. Normalizing isn't + // necessary in that case, the path should already be absolute. + filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); + var ret = fs.readFileSync(filename); + return ret; + }; + readAsync = (filename, binary = true) => { + // See the comment in the `readBinary` function. + filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); + return new Promise((resolve, reject) => { + fs.readFile(filename, binary ? undefined : "utf8", (err, data) => { + if (err) reject(err); else resolve(binary ? data.buffer : data); + }); + }); + }; + // end include: node_shell_read.js + if (!Module["thisProgram"] && process.argv.length > 1) { + thisProgram = process.argv[1].replace(/\\/g, "/"); + } + arguments_ = process.argv.slice(2); + if (typeof module != "undefined") { + module["exports"] = Module; + } + quit_ = (status, toThrow) => { + process.exitCode = status; + throw toThrow; + }; +} else // Note that this includes Node.js workers when relevant (pthreads is enabled). +// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and +// ENVIRONMENT_IS_NODE. +if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + if (ENVIRONMENT_IS_WORKER) { + // Check worker, not web, since window could be polyfilled + scriptDirectory = self.location.href; + } else if (typeof document != "undefined" && document.currentScript) { + // web + scriptDirectory = document.currentScript.src; + } + // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. + // otherwise, slice off the final part of the url to find the script directory. + // if scriptDirectory does not contain a slash, lastIndexOf will return -1, + // and scriptDirectory will correctly be replaced with an empty string. + // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), + // they are removed because they could contain a slash. + if (scriptDirectory.startsWith("blob:")) { + scriptDirectory = ""; + } else { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); + } + { + // include: web_or_worker_shell_read.js + if (ENVIRONMENT_IS_WORKER) { + readBinary = url => { + var xhr = new XMLHttpRequest; + xhr.open("GET", url, false); + xhr.responseType = "arraybuffer"; + xhr.send(null); + return new Uint8Array(/** @type{!ArrayBuffer} */ (xhr.response)); + }; + } + readAsync = url => { + // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. + // See https://github.com/github/fetch/pull/92#issuecomment-140665932 + // Cordova or Electron apps are typically loaded from a file:// url. + // So use XHR on webview if URL is a file URL. + if (isFileURI(url)) { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest; + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; + xhr.onload = () => { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { + // file URLs can return 0 + resolve(xhr.response); + return; + } + reject(xhr.status); + }; + xhr.onerror = reject; + xhr.send(null); + }); + } + return fetch(url, { + credentials: "same-origin" + }).then(response => { + if (response.ok) { + return response.arrayBuffer(); + } + return Promise.reject(new Error(response.status + " : " + response.url)); + }); + }; + } +} else // end include: web_or_worker_shell_read.js +{} + +var out = Module["print"] || console.log.bind(console); + +var err = Module["printErr"] || console.error.bind(console); + +// Merge back in the overrides +Object.assign(Module, moduleOverrides); + +// Free the object hierarchy contained in the overrides, this lets the GC +// reclaim data used. +moduleOverrides = null; + +// Emit code to handle expected values on the Module object. This applies Module.x +// to the proper local x. This has two benefits: first, we only emit it if it is +// expected to arrive, and second, by using a local everywhere else that can be +// minified. +if (Module["arguments"]) arguments_ = Module["arguments"]; + +if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; + +// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message +// end include: shell.js +// include: preamble.js +// === Preamble library stuff === +// Documentation for the public APIs defined in this file must be updated in: +// site/source/docs/api_reference/preamble.js.rst +// A prebuilt local version of the documentation is available at: +// site/build/text/docs/api_reference/preamble.js.txt +// You can also build docs locally as HTML or other formats in site/ +// An online HTML version (which may be of a different version of Emscripten) +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html +var wasmBinary = Module["wasmBinary"]; + +// Wasm globals +var wasmMemory; + +//======================================== +// Runtime essentials +//======================================== +// whether we are quitting the application. no code should run after this. +// set in exit() and abort() +var ABORT = false; + +// set by exit() and abort(). Passed to 'onExit' handler. +// NOTE: This is also used as the process return code code in shell environments +// but only when noExitRuntime is false. +var EXITSTATUS; + +// Memory management +var /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /** @type {!Float64Array} */ HEAPF64; + +// include: runtime_shared.js +function updateMemoryViews() { + var b = wasmMemory.buffer; + Module["HEAP8"] = HEAP8 = new Int8Array(b); + Module["HEAP16"] = HEAP16 = new Int16Array(b); + Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); + Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); + Module["HEAP32"] = HEAP32 = new Int32Array(b); + Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); + Module["HEAPF32"] = HEAPF32 = new Float32Array(b); + Module["HEAPF64"] = HEAPF64 = new Float64Array(b); +} + +// end include: runtime_shared.js +// include: runtime_stack_check.js +// end include: runtime_stack_check.js +var __ATPRERUN__ = []; + +// functions called before the runtime is initialized +var __ATINIT__ = []; + +// functions called during startup +var __ATMAIN__ = []; + +// functions called during shutdown +var __ATPOSTRUN__ = []; + +// functions called after the main() is called +var runtimeInitialized = false; + +function preRun() { + var preRuns = Module["preRun"]; + if (preRuns) { + if (typeof preRuns == "function") preRuns = [ preRuns ]; + preRuns.forEach(addOnPreRun); + } + callRuntimeCallbacks(__ATPRERUN__); +} + +function initRuntime() { + runtimeInitialized = true; + if (!Module["noFSInit"] && !FS.initialized) FS.init(); + FS.ignorePermissions = false; + TTY.init(); + callRuntimeCallbacks(__ATINIT__); +} + +function preMain() { + callRuntimeCallbacks(__ATMAIN__); +} + +function postRun() { + var postRuns = Module["postRun"]; + if (postRuns) { + if (typeof postRuns == "function") postRuns = [ postRuns ]; + postRuns.forEach(addOnPostRun); + } + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} + +// include: runtime_math.js +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc +// end include: runtime_math.js +// A counter of dependencies for calling run(). If we need to +// do asynchronous work before running, increment this and +// decrement it. Incrementing must happen in a place like +// Module.preRun (used by emcc to add file preloading). +// Note that you can add dependencies in preRun, even though +// it happens right before run - run will be postponed until +// the dependencies are met. +var runDependencies = 0; + +var runDependencyWatcher = null; + +var dependenciesFulfilled = null; + +// overridden to take different actions when all run dependencies are fulfilled +function getUniqueRunDependency(id) { + return id; +} + +function addRunDependency(id) { + runDependencies++; + Module["monitorRunDependencies"]?.(runDependencies); +} + +function removeRunDependency(id) { + runDependencies--; + Module["monitorRunDependencies"]?.(runDependencies); + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); + } + } +} + +/** @param {string|number=} what */ function abort(what) { + Module["onAbort"]?.(what); + what = "Aborted(" + what + ")"; + // TODO(sbc): Should we remove printing and leave it up to whoever + // catches the exception? + err(what); + ABORT = true; + what += ". Build with -sASSERTIONS for more info."; + // Use a wasm runtime error, because a JS error might be seen as a foreign + // exception, which means we'd run destructors on it. We need the error to + // simply make the program stop. + // FIXME This approach does not work in Wasm EH because it currently does not assume + // all RuntimeErrors are from traps; it decides whether a RuntimeError is from + // a trap or not based on a hidden field within the object. So at the moment + // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that + // allows this in the wasm spec. + // Suppress closure compiler warning here. Closure compiler's builtin extern + // definition for WebAssembly.RuntimeError claims it takes no arguments even + // though it can. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. + /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); + // Throw the error whether or not MODULARIZE is set because abort is used + // in code paths apart from instantiation where an exception is expected + // to be thrown when abort is called. + throw e; +} + +// include: memoryprofiler.js +// end include: memoryprofiler.js +// include: URIUtils.js +// Prefix of data URIs emitted by SINGLE_FILE and related options. +var dataURIPrefix = "data:application/octet-stream;base64,"; + +/** + * Indicates whether filename is a base64 data URI. + * @noinline + */ var isDataURI = filename => filename.startsWith(dataURIPrefix); + +/** + * Indicates whether filename is delivered via file protocol (as opposed to http/https) + * @noinline + */ var isFileURI = filename => filename.startsWith("file://"); + +// end include: URIUtils.js +// include: runtime_exceptions.js +// end include: runtime_exceptions.js +function findWasmBinary() { + var f = "tsf.wasm"; + if (!isDataURI(f)) { + return locateFile(f); + } + return f; +} + +var wasmBinaryFile; + +function getBinarySync(file) { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + if (readBinary) { + return readBinary(file); + } + throw "both async and sync fetching of the wasm failed"; +} + +function getBinaryPromise(binaryFile) { + // If we don't have the binary yet, load it asynchronously using readAsync. + if (!wasmBinary) { + // Fetch the binary using readAsync + return readAsync(binaryFile).then(response => new Uint8Array(/** @type{!ArrayBuffer} */ (response)), // Fall back to getBinarySync if readAsync fails + () => getBinarySync(binaryFile)); + } + // Otherwise, getBinarySync should be able to get it synchronously + return Promise.resolve().then(() => getBinarySync(binaryFile)); +} + +function instantiateArrayBuffer(binaryFile, imports, receiver) { + return getBinaryPromise(binaryFile).then(binary => WebAssembly.instantiate(binary, imports)).then(receiver, reason => { + err(`failed to asynchronously prepare wasm: ${reason}`); + abort(reason); + }); +} + +function instantiateAsync(binary, binaryFile, imports, callback) { + if (!binary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(binaryFile) && // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. + !isFileURI(binaryFile) && // Avoid instantiateStreaming() on Node.js environment for now, as while + // Node.js v18.1.0 implements it, it does not have a full fetch() + // implementation yet. + // Reference: + // https://github.com/emscripten-core/emscripten/pull/16917 + !ENVIRONMENT_IS_NODE && typeof fetch == "function") { + return fetch(binaryFile, { + credentials: "same-origin" + }).then(response => { + // Suppress closure warning here since the upstream definition for + // instantiateStreaming only allows Promise rather than + // an actual Response. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. + /** @suppress {checkTypes} */ var result = WebAssembly.instantiateStreaming(response, imports); + return result.then(callback, function(reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + err(`wasm streaming compile failed: ${reason}`); + err("falling back to ArrayBuffer instantiation"); + return instantiateArrayBuffer(binaryFile, imports, callback); + }); + }); + } + return instantiateArrayBuffer(binaryFile, imports, callback); +} + +function getWasmImports() { + // prepare imports + return { + "env": wasmImports, + "wasi_snapshot_preview1": wasmImports + }; +} + +// Create the wasm instance. +// Receives the wasm imports, returns the exports. +function createWasm() { + var info = getWasmImports(); + // Load the wasm module and create an instance of using native support in the JS engine. + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { + wasmExports = instance.exports; + wasmMemory = wasmExports["memory"]; + updateMemoryViews(); + addOnInit(wasmExports["__wasm_call_ctors"]); + removeRunDependency("wasm-instantiate"); + return wasmExports; + } + // wait for the pthread pool (if any) + addRunDependency("wasm-instantiate"); + // Prefer streaming instantiation if available. + function receiveInstantiationResult(result) { + // 'result' is a ResultObject object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called + // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. + // When the regression is fixed, can restore the above PTHREADS-enabled path. + receiveInstance(result["instance"]); + } + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback + // to manually instantiate the Wasm module themselves. This allows pages to + // run the instantiation parallel to any other async startup actions they are + // performing. + // Also pthreads and wasm workers initialize the wasm instance through this + // path. + if (Module["instantiateWasm"]) { + try { + return Module["instantiateWasm"](info, receiveInstance); + } catch (e) { + err(`Module.instantiateWasm callback failed with error: ${e}`); + return false; + } + } + wasmBinaryFile ??= findWasmBinary(); + instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult); + return {}; +} + +// Globals used by JS i64 conversions (see makeSetValue) +var tempDouble; + +var tempI64; + +// include: runtime_debug.js +// end include: runtime_debug.js +// === Body === +// end include: preamble.js +/** @constructor */ function ExitStatus(status) { + this.name = "ExitStatus"; + this.message = `Program terminated with exit(${status})`; + this.status = status; +} + +var callRuntimeCallbacks = callbacks => { + // Pass the module as the first argument. + callbacks.forEach(f => f(Module)); +}; + +var noExitRuntime = Module["noExitRuntime"] || true; + +/** @suppress {duplicate } */ function syscallGetVarargI() { + // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. + var ret = HEAP32[((+SYSCALLS.varargs) >> 2)]; + SYSCALLS.varargs += 4; + return ret; +} + +var syscallGetVarargP = syscallGetVarargI; + +var PATH = { + isAbs: path => path.charAt(0) === "/", + splitPath: filename => { + var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + return splitPathRe.exec(filename).slice(1); + }, + normalizeArray: (parts, allowAboveRoot) => { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === ".") { + parts.splice(i, 1); + } else if (last === "..") { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (;up; up--) { + parts.unshift(".."); + } + } + return parts; + }, + normalize: path => { + var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === "/"; + // Normalize the path + path = PATH.normalizeArray(path.split("/").filter(p => !!p), !isAbsolute).join("/"); + if (!path && !isAbsolute) { + path = "."; + } + if (path && trailingSlash) { + path += "/"; + } + return (isAbsolute ? "/" : "") + path; + }, + dirname: path => { + var result = PATH.splitPath(path), root = result[0], dir = result[1]; + if (!root && !dir) { + // No dirname whatsoever + return "."; + } + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + return root + dir; + }, + basename: path => { + // EMSCRIPTEN return '/'' for '/', not an empty string + if (path === "/") return "/"; + path = PATH.normalize(path); + path = path.replace(/\/$/, ""); + var lastSlash = path.lastIndexOf("/"); + if (lastSlash === -1) return path; + return path.substr(lastSlash + 1); + }, + join: (...paths) => PATH.normalize(paths.join("/")), + join2: (l, r) => PATH.normalize(l + "/" + r) +}; + +var initRandomFill = () => { + if (typeof crypto == "object" && typeof crypto["getRandomValues"] == "function") { + // for modern web browsers + return view => crypto.getRandomValues(view); + } else if (ENVIRONMENT_IS_NODE) { + // for nodejs with or without crypto support included + try { + var crypto_module = require("crypto"); + var randomFillSync = crypto_module["randomFillSync"]; + if (randomFillSync) { + // nodejs with LTS crypto support + return view => crypto_module["randomFillSync"](view); + } + // very old nodejs with the original crypto API + var randomBytes = crypto_module["randomBytes"]; + return view => (view.set(randomBytes(view.byteLength)), // Return the original view to match modern native implementations. + view); + } catch (e) {} + } + // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 + abort("initRandomDevice"); +}; + +var randomFill = view => (randomFill = initRandomFill())(view); + +var PATH_FS = { + resolve: (...args) => { + var resolvedPath = "", resolvedAbsolute = false; + for (var i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? args[i] : FS.cwd(); + // Skip empty and invalid entries + if (typeof path != "string") { + throw new TypeError("Arguments to path.resolve must be strings"); + } else if (!path) { + return ""; + } + // an invalid portion invalidates the whole thing + resolvedPath = path + "/" + resolvedPath; + resolvedAbsolute = PATH.isAbs(path); + } + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + resolvedPath = PATH.normalizeArray(resolvedPath.split("/").filter(p => !!p), !resolvedAbsolute).join("/"); + return ((resolvedAbsolute ? "/" : "") + resolvedPath) || "."; + }, + relative: (from, to) => { + from = PATH_FS.resolve(from).substr(1); + to = PATH_FS.resolve(to).substr(1); + function trim(arr) { + var start = 0; + for (;start < arr.length; start++) { + if (arr[start] !== "") break; + } + var end = arr.length - 1; + for (;end >= 0; end--) { + if (arr[end] !== "") break; + } + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + var fromParts = trim(from.split("/")); + var toParts = trim(to.split("/")); + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push(".."); + } + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + return outputParts.join("/"); + } +}; + +var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder : undefined; + +/** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number=} idx + * @param {number=} maxBytesToRead + * @return {string} + */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. Also, use the length info to avoid running tiny + // strings through TextDecoder, since .subarray() allocates garbage. + // (As a tiny code save trick, compare endPtr against endIdx using a negation, + // so that undefined/NaN means Infinity) + while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); + } + var str = ""; + // If building with TextDecoder, we have already computed the string length + // above, so test loop end condition against that + while (idx < endPtr) { + // For UTF8 byte structure, see: + // http://en.wikipedia.org/wiki/UTF-8#Description + // https://www.ietf.org/rfc/rfc2279.txt + // https://tools.ietf.org/html/rfc3629 + var u0 = heapOrArray[idx++]; + if (!(u0 & 128)) { + str += String.fromCharCode(u0); + continue; + } + var u1 = heapOrArray[idx++] & 63; + if ((u0 & 224) == 192) { + str += String.fromCharCode(((u0 & 31) << 6) | u1); + continue; + } + var u2 = heapOrArray[idx++] & 63; + if ((u0 & 240) == 224) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); + } + if (u0 < 65536) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 65536; + str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); + } + } + return str; +}; + +var FS_stdin_getChar_buffer = []; + +var lengthBytesUTF8 = str => { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code + // unit, not a Unicode code point of the character! So decode + // UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var c = str.charCodeAt(i); + // possibly a lead surrogate + if (c <= 127) { + len++; + } else if (c <= 2047) { + len += 2; + } else if (c >= 55296 && c <= 57343) { + len += 4; + ++i; + } else { + len += 3; + } + } + return len; +}; + +var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { + // Parameter maxBytesToWrite is not optional. Negative values, 0, null, + // undefined and false each don't write out any bytes. + if (!(maxBytesToWrite > 0)) return 0; + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; + // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code + // unit, not a Unicode code point of the character! So decode + // UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description + // and https://www.ietf.org/rfc/rfc2279.txt + // and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); + // possibly a lead surrogate + if (u >= 55296 && u <= 57343) { + var u1 = str.charCodeAt(++i); + u = 65536 + ((u & 1023) << 10) | (u1 & 1023); + } + if (u <= 127) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 2047) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 192 | (u >> 6); + heap[outIdx++] = 128 | (u & 63); + } else if (u <= 65535) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 224 | (u >> 12); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + heap[outIdx++] = 240 | (u >> 18); + heap[outIdx++] = 128 | ((u >> 12) & 63); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } + } + // Null-terminate the pointer to the buffer. + heap[outIdx] = 0; + return outIdx - startIdx; +}; + +/** @type {function(string, boolean=, number=)} */ function intArrayFromString(stringy, dontAddNull, length) { + var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1; + var u8array = new Array(len); + var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); + if (dontAddNull) u8array.length = numBytesWritten; + return u8array; +} + +var FS_stdin_getChar = () => { + if (!FS_stdin_getChar_buffer.length) { + var result = null; + if (ENVIRONMENT_IS_NODE) { + // we will read data by chunks of BUFSIZE + var BUFSIZE = 256; + var buf = Buffer.alloc(BUFSIZE); + var bytesRead = 0; + // For some reason we must suppress a closure warning here, even though + // fd definitely exists on process.stdin, and is even the proper way to + // get the fd of stdin, + // https://github.com/nodejs/help/issues/2136#issuecomment-523649904 + // This started to happen after moving this logic out of library_tty.js, + // so it is related to the surrounding code in some unclear manner. + /** @suppress {missingProperties} */ var fd = process.stdin.fd; + try { + bytesRead = fs.readSync(fd, buf, 0, BUFSIZE); + } catch (e) { + // Cross-platform differences: on Windows, reading EOF throws an + // exception, but on other OSes, reading EOF returns 0. Uniformize + // behavior by treating the EOF exception to return 0. + if (e.toString().includes("EOF")) bytesRead = 0; else throw e; + } + if (bytesRead > 0) { + result = buf.slice(0, bytesRead).toString("utf-8"); + } + } else if (typeof window != "undefined" && typeof window.prompt == "function") { + // Browser. + result = window.prompt("Input: "); + // returns null on cancel + if (result !== null) { + result += "\n"; + } + } else {} + if (!result) { + return null; + } + FS_stdin_getChar_buffer = intArrayFromString(result, true); + } + return FS_stdin_getChar_buffer.shift(); +}; + +var TTY = { + ttys: [], + init() {}, + // https://github.com/emscripten-core/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // currently, FS.init does not distinguish if process.stdin is a file or TTY + // // device, it always assumes it's a TTY device. because of this, we're forcing + // // process.stdin to UTF8 encoding to at least make stdin reading compatible + // // with text files until FS.init can be refactored. + // process.stdin.setEncoding('utf8'); + // } + shutdown() {}, + // https://github.com/emscripten-core/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? + // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation + // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? + // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle + // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call + // process.stdin.pause(); + // } + register(dev, ops) { + TTY.ttys[dev] = { + input: [], + output: [], + ops + }; + FS.registerDevice(dev, TTY.stream_ops); + }, + stream_ops: { + open(stream) { + var tty = TTY.ttys[stream.node.rdev]; + if (!tty) { + throw new FS.ErrnoError(43); + } + stream.tty = tty; + stream.seekable = false; + }, + close(stream) { + // flush any pending line data + stream.tty.ops.fsync(stream.tty); + }, + fsync(stream) { + stream.tty.ops.fsync(stream.tty); + }, + read(stream, buffer, offset, length, pos) { + /* ignored */ if (!stream.tty || !stream.tty.ops.get_char) { + throw new FS.ErrnoError(60); + } + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(6); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write(stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) { + throw new FS.ErrnoError(60); + } + try { + for (var i = 0; i < length; i++) { + stream.tty.ops.put_char(stream.tty, buffer[offset + i]); + } + } catch (e) { + throw new FS.ErrnoError(29); + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }, + default_tty_ops: { + get_char(tty) { + return FS_stdin_getChar(); + }, + put_char(tty, val) { + if (val === null || val === 10) { + out(UTF8ArrayToString(tty.output)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); + } + }, + // val == 0 would cut text output off in the middle. + fsync(tty) { + if (tty.output && tty.output.length > 0) { + out(UTF8ArrayToString(tty.output)); + tty.output = []; + } + }, + ioctl_tcgets(tty) { + // typical setting + return { + c_iflag: 25856, + c_oflag: 5, + c_cflag: 191, + c_lflag: 35387, + c_cc: [ 3, 28, 127, 21, 4, 0, 1, 0, 17, 19, 26, 0, 18, 15, 23, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }; + }, + ioctl_tcsets(tty, optional_actions, data) { + // currently just ignore + return 0; + }, + ioctl_tiocgwinsz(tty) { + return [ 24, 80 ]; + } + }, + default_tty1_ops: { + put_char(tty, val) { + if (val === null || val === 10) { + err(UTF8ArrayToString(tty.output)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); + } + }, + fsync(tty) { + if (tty.output && tty.output.length > 0) { + err(UTF8ArrayToString(tty.output)); + tty.output = []; + } + } + } +}; + +var mmapAlloc = size => { + abort(); +}; + +var MEMFS = { + ops_table: null, + mount(mount) { + return MEMFS.createNode(null, "/", 16384 | 511, /* 0777 */ 0); + }, + createNode(parent, name, mode, dev) { + if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { + // no supported + throw new FS.ErrnoError(63); + } + MEMFS.ops_table ||= { + dir: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + lookup: MEMFS.node_ops.lookup, + mknod: MEMFS.node_ops.mknod, + rename: MEMFS.node_ops.rename, + unlink: MEMFS.node_ops.unlink, + rmdir: MEMFS.node_ops.rmdir, + readdir: MEMFS.node_ops.readdir, + symlink: MEMFS.node_ops.symlink + }, + stream: { + llseek: MEMFS.stream_ops.llseek + } + }, + file: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: { + llseek: MEMFS.stream_ops.llseek, + read: MEMFS.stream_ops.read, + write: MEMFS.stream_ops.write, + allocate: MEMFS.stream_ops.allocate, + mmap: MEMFS.stream_ops.mmap, + msync: MEMFS.stream_ops.msync + } + }, + link: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + readlink: MEMFS.node_ops.readlink + }, + stream: {} + }, + chrdev: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: FS.chrdev_stream_ops + } + }; + var node = FS.createNode(parent, name, mode, dev); + if (FS.isDir(node.mode)) { + node.node_ops = MEMFS.ops_table.dir.node; + node.stream_ops = MEMFS.ops_table.dir.stream; + node.contents = {}; + } else if (FS.isFile(node.mode)) { + node.node_ops = MEMFS.ops_table.file.node; + node.stream_ops = MEMFS.ops_table.file.stream; + node.usedBytes = 0; + // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. + // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred + // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size + // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. + node.contents = null; + } else if (FS.isLink(node.mode)) { + node.node_ops = MEMFS.ops_table.link.node; + node.stream_ops = MEMFS.ops_table.link.stream; + } else if (FS.isChrdev(node.mode)) { + node.node_ops = MEMFS.ops_table.chrdev.node; + node.stream_ops = MEMFS.ops_table.chrdev.stream; + } + node.timestamp = Date.now(); + // add the new node to the parent + if (parent) { + parent.contents[name] = node; + parent.timestamp = node.timestamp; + } + return node; + }, + getFileDataAsTypedArray(node) { + if (!node.contents) return new Uint8Array(0); + if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); + // Make sure to not return excess unused bytes. + return new Uint8Array(node.contents); + }, + expandFileStorage(node, newCapacity) { + var prevCapacity = node.contents ? node.contents.length : 0; + if (prevCapacity >= newCapacity) return; + // No need to expand, the storage was already large enough. + // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. + // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to + // avoid overshooting the allocation cap by a very large margin. + var CAPACITY_DOUBLING_MAX = 1024 * 1024; + newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0); + if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); + // At minimum allocate 256b for each file when expanding. + var oldContents = node.contents; + node.contents = new Uint8Array(newCapacity); + // Allocate new storage. + if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); + }, + // Copy old data over to the new storage. + resizeFileStorage(node, newSize) { + if (node.usedBytes == newSize) return; + if (newSize == 0) { + node.contents = null; + // Fully decommit when requesting a resize to zero. + node.usedBytes = 0; + } else { + var oldContents = node.contents; + node.contents = new Uint8Array(newSize); + // Allocate new storage. + if (oldContents) { + node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); + } + // Copy old data over to the new storage. + node.usedBytes = newSize; + } + }, + node_ops: { + getattr(node) { + var attr = {}; + // device numbers reuse inode numbers. + attr.dev = FS.isChrdev(node.mode) ? node.id : 1; + attr.ino = node.id; + attr.mode = node.mode; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + if (FS.isDir(node.mode)) { + attr.size = 4096; + } else if (FS.isFile(node.mode)) { + attr.size = node.usedBytes; + } else if (FS.isLink(node.mode)) { + attr.size = node.link.length; + } else { + attr.size = 0; + } + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), + // but this is not required by the standard. + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr(node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + if (attr.size !== undefined) { + MEMFS.resizeFileStorage(node, attr.size); + } + }, + lookup(parent, name) { + throw FS.genericErrors[44]; + }, + mknod(parent, name, mode, dev) { + return MEMFS.createNode(parent, name, mode, dev); + }, + rename(old_node, new_dir, new_name) { + // if we're overwriting a directory at new_name, make sure it's empty. + if (FS.isDir(old_node.mode)) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (new_node) { + for (var i in new_node.contents) { + throw new FS.ErrnoError(55); + } + } + } + // do the internal rewiring + delete old_node.parent.contents[old_node.name]; + old_node.parent.timestamp = Date.now(); + old_node.name = new_name; + new_dir.contents[new_name] = old_node; + new_dir.timestamp = old_node.parent.timestamp; + }, + unlink(parent, name) { + delete parent.contents[name]; + parent.timestamp = Date.now(); + }, + rmdir(parent, name) { + var node = FS.lookupNode(parent, name); + for (var i in node.contents) { + throw new FS.ErrnoError(55); + } + delete parent.contents[name]; + parent.timestamp = Date.now(); + }, + readdir(node) { + var entries = [ ".", ".." ]; + for (var key of Object.keys(node.contents)) { + entries.push(key); + } + return entries; + }, + symlink(parent, newname, oldpath) { + var node = MEMFS.createNode(parent, newname, 511 | /* 0777 */ 40960, 0); + node.link = oldpath; + return node; + }, + readlink(node) { + if (!FS.isLink(node.mode)) { + throw new FS.ErrnoError(28); + } + return node.link; + } + }, + stream_ops: { + read(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + if (position >= stream.node.usedBytes) return 0; + var size = Math.min(stream.node.usedBytes - position, length); + if (size > 8 && contents.subarray) { + // non-trivial, and typed array + buffer.set(contents.subarray(position, position + size), offset); + } else { + for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; + } + return size; + }, + write(stream, buffer, offset, length, position, canOwn) { + if (!length) return 0; + var node = stream.node; + node.timestamp = Date.now(); + if (buffer.subarray && (!node.contents || node.contents.subarray)) { + // This write is from a typed array to a typed array? + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + return length; + } else if (node.usedBytes === 0 && position === 0) { + // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + return length; + } else if (position + length <= node.usedBytes) { + // Writing to an already allocated and used subrange of the file? + node.contents.set(buffer.subarray(offset, offset + length), position); + return length; + } + } + // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. + MEMFS.expandFileStorage(node, position + length); + if (node.contents.subarray && buffer.subarray) { + // Use typed array write which is available. + node.contents.set(buffer.subarray(offset, offset + length), position); + } else { + for (var i = 0; i < length; i++) { + node.contents[position + i] = buffer[offset + i]; + } + } + node.usedBytes = Math.max(node.usedBytes, position + length); + return length; + }, + llseek(stream, offset, whence) { + var position = offset; + if (whence === 1) { + position += stream.position; + } else if (whence === 2) { + if (FS.isFile(stream.node.mode)) { + position += stream.node.usedBytes; + } + } + if (position < 0) { + throw new FS.ErrnoError(28); + } + return position; + }, + allocate(stream, offset, length) { + MEMFS.expandFileStorage(stream.node, offset + length); + stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length); + }, + mmap(stream, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + var ptr; + var allocated; + var contents = stream.node.contents; + // Only make a new copy when MAP_PRIVATE is specified. + if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { + // We can't emulate MAP_SHARED when the file is not backed by the + // buffer we're mapping to (e.g. the HEAP buffer). + allocated = false; + ptr = contents.byteOffset; + } else { + allocated = true; + ptr = mmapAlloc(length); + if (!ptr) { + throw new FS.ErrnoError(48); + } + if (contents) { + // Try to avoid unnecessary slices. + if (position > 0 || position + length < contents.length) { + if (contents.subarray) { + contents = contents.subarray(position, position + length); + } else { + contents = Array.prototype.slice.call(contents, position, position + length); + } + } + HEAP8.set(contents, ptr); + } + } + return { + ptr, + allocated + }; + }, + msync(stream, buffer, offset, length, mmapFlags) { + MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); + // should we check if bytesWritten and length are the same? + return 0; + } + } +}; + +/** @param {boolean=} noRunDep */ var asyncLoad = (url, onload, onerror, noRunDep) => { + var dep = !noRunDep ? getUniqueRunDependency(`al ${url}`) : ""; + readAsync(url).then(arrayBuffer => { + onload(new Uint8Array(arrayBuffer)); + if (dep) removeRunDependency(dep); + }, err => { + if (onerror) { + onerror(); + } else { + throw `Loading data file "${url}" failed.`; + } + }); + if (dep) addRunDependency(dep); +}; + +var FS_createDataFile = (parent, name, fileData, canRead, canWrite, canOwn) => { + FS.createDataFile(parent, name, fileData, canRead, canWrite, canOwn); +}; + +var preloadPlugins = Module["preloadPlugins"] || []; + +var FS_handledByPreloadPlugin = (byteArray, fullname, finish, onerror) => { + // Ensure plugins are ready. + if (typeof Browser != "undefined") Browser.init(); + var handled = false; + preloadPlugins.forEach(plugin => { + if (handled) return; + if (plugin["canHandle"](fullname)) { + plugin["handle"](byteArray, fullname, finish, onerror); + handled = true; + } + }); + return handled; +}; + +var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { + // TODO we should allow people to just pass in a complete filename instead + // of parent and name being that we just join them anyways + var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; + var dep = getUniqueRunDependency(`cp ${fullname}`); + // might have several active requests for the same fullname + function processData(byteArray) { + function finish(byteArray) { + preFinish?.(); + if (!dontCreateFile) { + FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); + } + onload?.(); + removeRunDependency(dep); + } + if (FS_handledByPreloadPlugin(byteArray, fullname, finish, () => { + onerror?.(); + removeRunDependency(dep); + })) { + return; + } + finish(byteArray); + } + addRunDependency(dep); + if (typeof url == "string") { + asyncLoad(url, processData, onerror); + } else { + processData(url); + } +}; + +var FS_modeStringToFlags = str => { + var flagModes = { + "r": 0, + "r+": 2, + "w": 512 | 64 | 1, + "w+": 512 | 64 | 2, + "a": 1024 | 64 | 1, + "a+": 1024 | 64 | 2 + }; + var flags = flagModes[str]; + if (typeof flags == "undefined") { + throw new Error(`Unknown file open mode: ${str}`); + } + return flags; +}; + +var FS_getMode = (canRead, canWrite) => { + var mode = 0; + if (canRead) mode |= 292 | 73; + if (canWrite) mode |= 146; + return mode; +}; + +var FS = { + root: null, + mounts: [], + devices: {}, + streams: [], + nextInode: 1, + nameTable: null, + currentPath: "/", + initialized: false, + ignorePermissions: true, + ErrnoError: class { + // We set the `name` property to be able to identify `FS.ErrnoError` + // - the `name` is a standard ECMA-262 property of error objects. Kind of good to have it anyway. + // - when using PROXYFS, an error can come from an underlying FS + // as different FS objects have their own FS.ErrnoError each, + // the test `err instanceof FS.ErrnoError` won't detect an error coming from another filesystem, causing bugs. + // we'll use the reliable test `err.name == "ErrnoError"` instead + constructor(errno) { + // TODO(sbc): Use the inline member declaration syntax once we + // support it in acorn and closure. + this.name = "ErrnoError"; + this.errno = errno; + } + }, + genericErrors: {}, + filesystems: null, + syncFSRequests: 0, + readFiles: {}, + FSStream: class { + constructor() { + // TODO(https://github.com/emscripten-core/emscripten/issues/21414): + // Use inline field declarations. + this.shared = {}; + } + get object() { + return this.node; + } + set object(val) { + this.node = val; + } + get isRead() { + return (this.flags & 2097155) !== 1; + } + get isWrite() { + return (this.flags & 2097155) !== 0; + } + get isAppend() { + return (this.flags & 1024); + } + get flags() { + return this.shared.flags; + } + set flags(val) { + this.shared.flags = val; + } + get position() { + return this.shared.position; + } + set position(val) { + this.shared.position = val; + } + }, + FSNode: class { + constructor(parent, name, mode, rdev) { + if (!parent) { + parent = this; + } + // root node sets parent to itself + this.parent = parent; + this.mount = parent.mount; + this.mounted = null; + this.id = FS.nextInode++; + this.name = name; + this.mode = mode; + this.node_ops = {}; + this.stream_ops = {}; + this.rdev = rdev; + this.readMode = 292 | 73; + this.writeMode = 146; + } + get read() { + return (this.mode & this.readMode) === this.readMode; + } + set read(val) { + val ? this.mode |= this.readMode : this.mode &= ~this.readMode; + } + get write() { + return (this.mode & this.writeMode) === this.writeMode; + } + set write(val) { + val ? this.mode |= this.writeMode : this.mode &= ~this.writeMode; + } + get isFolder() { + return FS.isDir(this.mode); + } + get isDevice() { + return FS.isChrdev(this.mode); + } + }, + lookupPath(path, opts = {}) { + path = PATH_FS.resolve(path); + if (!path) return { + path: "", + node: null + }; + var defaults = { + follow_mount: true, + recurse_count: 0 + }; + opts = Object.assign(defaults, opts); + if (opts.recurse_count > 8) { + // max recursive lookup of 8 + throw new FS.ErrnoError(32); + } + // split the absolute path + var parts = path.split("/").filter(p => !!p); + // start at the root + var current = FS.root; + var current_path = "/"; + for (var i = 0; i < parts.length; i++) { + var islast = (i === parts.length - 1); + if (islast && opts.parent) { + // stop resolving + break; + } + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join2(current_path, parts[i]); + // jump to the mount's root node if this is a mountpoint + if (FS.isMountpoint(current)) { + if (!islast || (islast && opts.follow_mount)) { + current = current.mounted.root; + } + } + // by default, lookupPath will not follow a symlink if it is the final path component. + // setting opts.follow = true will override this behavior. + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = FS.readlink(current_path); + current_path = PATH_FS.resolve(PATH.dirname(current_path), link); + var lookup = FS.lookupPath(current_path, { + recurse_count: opts.recurse_count + 1 + }); + current = lookup.node; + if (count++ > 40) { + // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + throw new FS.ErrnoError(32); + } + } + } + } + return { + path: current_path, + node: current + }; + }, + getPath(node) { + var path; + while (true) { + if (FS.isRoot(node)) { + var mount = node.mount.mountpoint; + if (!path) return mount; + return mount[mount.length - 1] !== "/" ? `${mount}/${path}` : mount + path; + } + path = path ? `${node.name}/${path}` : node.name; + node = node.parent; + } + }, + hashName(parentid, name) { + var hash = 0; + for (var i = 0; i < name.length; i++) { + hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; + } + return ((parentid + hash) >>> 0) % FS.nameTable.length; + }, + hashAddNode(node) { + var hash = FS.hashName(node.parent.id, node.name); + node.name_next = FS.nameTable[hash]; + FS.nameTable[hash] = node; + }, + hashRemoveNode(node) { + var hash = FS.hashName(node.parent.id, node.name); + if (FS.nameTable[hash] === node) { + FS.nameTable[hash] = node.name_next; + } else { + var current = FS.nameTable[hash]; + while (current) { + if (current.name_next === node) { + current.name_next = node.name_next; + break; + } + current = current.name_next; + } + } + }, + lookupNode(parent, name) { + var errCode = FS.mayLookup(parent); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + var hash = FS.hashName(parent.id, name); + for (var node = FS.nameTable[hash]; node; node = node.name_next) { + var nodeName = node.name; + if (node.parent.id === parent.id && nodeName === name) { + return node; + } + } + // if we failed to find it in the cache, call into the VFS + return FS.lookup(parent, name); + }, + createNode(parent, name, mode, rdev) { + var node = new FS.FSNode(parent, name, mode, rdev); + FS.hashAddNode(node); + return node; + }, + destroyNode(node) { + FS.hashRemoveNode(node); + }, + isRoot(node) { + return node === node.parent; + }, + isMountpoint(node) { + return !!node.mounted; + }, + isFile(mode) { + return (mode & 61440) === 32768; + }, + isDir(mode) { + return (mode & 61440) === 16384; + }, + isLink(mode) { + return (mode & 61440) === 40960; + }, + isChrdev(mode) { + return (mode & 61440) === 8192; + }, + isBlkdev(mode) { + return (mode & 61440) === 24576; + }, + isFIFO(mode) { + return (mode & 61440) === 4096; + }, + isSocket(mode) { + return (mode & 49152) === 49152; + }, + flagsToPermissionString(flag) { + var perms = [ "r", "w", "rw" ][flag & 3]; + if ((flag & 512)) { + perms += "w"; + } + return perms; + }, + nodePermissions(node, perms) { + if (FS.ignorePermissions) { + return 0; + } + // return 0 if any user, group or owner bits are set. + if (perms.includes("r") && !(node.mode & 292)) { + return 2; + } else if (perms.includes("w") && !(node.mode & 146)) { + return 2; + } else if (perms.includes("x") && !(node.mode & 73)) { + return 2; + } + return 0; + }, + mayLookup(dir) { + if (!FS.isDir(dir.mode)) return 54; + var errCode = FS.nodePermissions(dir, "x"); + if (errCode) return errCode; + if (!dir.node_ops.lookup) return 2; + return 0; + }, + mayCreate(dir, name) { + try { + var node = FS.lookupNode(dir, name); + return 20; + } catch (e) {} + return FS.nodePermissions(dir, "wx"); + }, + mayDelete(dir, name, isdir) { + var node; + try { + node = FS.lookupNode(dir, name); + } catch (e) { + return e.errno; + } + var errCode = FS.nodePermissions(dir, "wx"); + if (errCode) { + return errCode; + } + if (isdir) { + if (!FS.isDir(node.mode)) { + return 54; + } + if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { + return 10; + } + } else { + if (FS.isDir(node.mode)) { + return 31; + } + } + return 0; + }, + mayOpen(node, flags) { + if (!node) { + return 44; + } + if (FS.isLink(node.mode)) { + return 32; + } else if (FS.isDir(node.mode)) { + if (FS.flagsToPermissionString(flags) !== "r" || // opening for write + (flags & 512)) { + // TODO: check for O_SEARCH? (== search for dir only) + return 31; + } + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + }, + MAX_OPEN_FDS: 4096, + nextfd() { + for (var fd = 0; fd <= FS.MAX_OPEN_FDS; fd++) { + if (!FS.streams[fd]) { + return fd; + } + } + throw new FS.ErrnoError(33); + }, + getStreamChecked(fd) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(8); + } + return stream; + }, + getStream: fd => FS.streams[fd], + createStream(stream, fd = -1) { + // clone it, so we can return an instance of FSStream + stream = Object.assign(new FS.FSStream, stream); + if (fd == -1) { + fd = FS.nextfd(); + } + stream.fd = fd; + FS.streams[fd] = stream; + return stream; + }, + closeStream(fd) { + FS.streams[fd] = null; + }, + dupStream(origStream, fd = -1) { + var stream = FS.createStream(origStream, fd); + stream.stream_ops?.dup?.(stream); + return stream; + }, + chrdev_stream_ops: { + open(stream) { + var device = FS.getDevice(stream.node.rdev); + // override node's stream ops with the device's + stream.stream_ops = device.stream_ops; + // forward the open call + stream.stream_ops.open?.(stream); + }, + llseek() { + throw new FS.ErrnoError(70); + } + }, + major: dev => ((dev) >> 8), + minor: dev => ((dev) & 255), + makedev: (ma, mi) => ((ma) << 8 | (mi)), + registerDevice(dev, ops) { + FS.devices[dev] = { + stream_ops: ops + }; + }, + getDevice: dev => FS.devices[dev], + getMounts(mount) { + var mounts = []; + var check = [ mount ]; + while (check.length) { + var m = check.pop(); + mounts.push(m); + check.push(...m.mounts); + } + return mounts; + }, + syncfs(populate, callback) { + if (typeof populate == "function") { + callback = populate; + populate = false; + } + FS.syncFSRequests++; + if (FS.syncFSRequests > 1) { + err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`); + } + var mounts = FS.getMounts(FS.root.mount); + var completed = 0; + function doCallback(errCode) { + FS.syncFSRequests--; + return callback(errCode); + } + function done(errCode) { + if (errCode) { + if (!done.errored) { + done.errored = true; + return doCallback(errCode); + } + return; + } + if (++completed >= mounts.length) { + doCallback(null); + } + } + // sync all mounts + mounts.forEach(mount => { + if (!mount.type.syncfs) { + return done(null); + } + mount.type.syncfs(mount, populate, done); + }); + }, + mount(type, opts, mountpoint) { + var root = mountpoint === "/"; + var pseudo = !mountpoint; + var node; + if (root && FS.root) { + throw new FS.ErrnoError(10); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { + follow_mount: false + }); + mountpoint = lookup.path; + // use the absolute path + node = lookup.node; + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError(54); + } + } + var mount = { + type, + opts, + mountpoint, + mounts: [] + }; + // create a root node for the fs + var mountRoot = type.mount(mount); + mountRoot.mount = mount; + mount.root = mountRoot; + if (root) { + FS.root = mountRoot; + } else if (node) { + // set as a mountpoint + node.mounted = mount; + // add the new mount to the current mount's children + if (node.mount) { + node.mount.mounts.push(mount); + } + } + return mountRoot; + }, + unmount(mountpoint) { + var lookup = FS.lookupPath(mountpoint, { + follow_mount: false + }); + if (!FS.isMountpoint(lookup.node)) { + throw new FS.ErrnoError(28); + } + // destroy the nodes for this mount, and all its child mounts + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + Object.keys(FS.nameTable).forEach(hash => { + var current = FS.nameTable[hash]; + while (current) { + var next = current.name_next; + if (mounts.includes(current.mount)) { + FS.destroyNode(current); + } + current = next; + } + }); + // no longer a mountpoint + node.mounted = null; + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); + node.mount.mounts.splice(idx, 1); + }, + lookup(parent, name) { + return parent.node_ops.lookup(parent, name); + }, + mknod(path, mode, dev) { + var lookup = FS.lookupPath(path, { + parent: true + }); + var parent = lookup.node; + var name = PATH.basename(path); + if (!name || name === "." || name === "..") { + throw new FS.ErrnoError(28); + } + var errCode = FS.mayCreate(parent, name); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.mknod) { + throw new FS.ErrnoError(63); + } + return parent.node_ops.mknod(parent, name, mode, dev); + }, + create(path, mode) { + mode = mode !== undefined ? mode : 438; + /* 0666 */ mode &= 4095; + mode |= 32768; + return FS.mknod(path, mode, 0); + }, + mkdir(path, mode) { + mode = mode !== undefined ? mode : 511; + /* 0777 */ mode &= 511 | 512; + mode |= 16384; + return FS.mknod(path, mode, 0); + }, + mkdirTree(path, mode) { + var dirs = path.split("/"); + var d = ""; + for (var i = 0; i < dirs.length; ++i) { + if (!dirs[i]) continue; + d += "/" + dirs[i]; + try { + FS.mkdir(d, mode); + } catch (e) { + if (e.errno != 20) throw e; + } + } + }, + mkdev(path, mode, dev) { + if (typeof dev == "undefined") { + dev = mode; + mode = 438; + } + /* 0666 */ mode |= 8192; + return FS.mknod(path, mode, dev); + }, + symlink(oldpath, newpath) { + if (!PATH_FS.resolve(oldpath)) { + throw new FS.ErrnoError(44); + } + var lookup = FS.lookupPath(newpath, { + parent: true + }); + var parent = lookup.node; + if (!parent) { + throw new FS.ErrnoError(44); + } + var newname = PATH.basename(newpath); + var errCode = FS.mayCreate(parent, newname); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.symlink) { + throw new FS.ErrnoError(63); + } + return parent.node_ops.symlink(parent, newname, oldpath); + }, + rename(old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + // parents must exist + var lookup, old_dir, new_dir; + // let the errors from non existent directories percolate up + lookup = FS.lookupPath(old_path, { + parent: true + }); + old_dir = lookup.node; + lookup = FS.lookupPath(new_path, { + parent: true + }); + new_dir = lookup.node; + if (!old_dir || !new_dir) throw new FS.ErrnoError(44); + // need to be part of the same mount + if (old_dir.mount !== new_dir.mount) { + throw new FS.ErrnoError(75); + } + // source must exist + var old_node = FS.lookupNode(old_dir, old_name); + // old path should not be an ancestor of the new path + var relative = PATH_FS.relative(old_path, new_dirname); + if (relative.charAt(0) !== ".") { + throw new FS.ErrnoError(28); + } + // new path should not be an ancestor of the old path + relative = PATH_FS.relative(new_path, old_dirname); + if (relative.charAt(0) !== ".") { + throw new FS.ErrnoError(55); + } + // see if the new path already exists + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + // early out if nothing needs to change + if (old_node === new_node) { + return; + } + // we'll need to delete the old entry + var isdir = FS.isDir(old_node.mode); + var errCode = FS.mayDelete(old_dir, old_name, isdir); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + // need delete permissions if we'll be overwriting. + // need create permissions if new doesn't already exist. + errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!old_dir.node_ops.rename) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { + throw new FS.ErrnoError(10); + } + // if we are going to change the parent, check write permissions + if (new_dir !== old_dir) { + errCode = FS.nodePermissions(old_dir, "w"); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + } + // remove the node from the lookup hash + FS.hashRemoveNode(old_node); + // do the underlying fs rename + try { + old_dir.node_ops.rename(old_node, new_dir, new_name); + // update old node (we do this here to avoid each backend + // needing to) + old_node.parent = new_dir; + } catch (e) { + throw e; + } finally { + // add the node back to the hash (in case node_ops.rename + // changed its name) + FS.hashAddNode(old_node); + } + }, + rmdir(path) { + var lookup = FS.lookupPath(path, { + parent: true + }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, true); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.rmdir) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + }, + readdir(path) { + var lookup = FS.lookupPath(path, { + follow: true + }); + var node = lookup.node; + if (!node.node_ops.readdir) { + throw new FS.ErrnoError(54); + } + return node.node_ops.readdir(node); + }, + unlink(path) { + var lookup = FS.lookupPath(path, { + parent: true + }); + var parent = lookup.node; + if (!parent) { + throw new FS.ErrnoError(44); + } + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, false); + if (errCode) { + // According to POSIX, we should map EISDIR to EPERM, but + // we instead do what Linux does (and we must, as we use + // the musl linux libc). + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.unlink) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + }, + readlink(path) { + var lookup = FS.lookupPath(path); + var link = lookup.node; + if (!link) { + throw new FS.ErrnoError(44); + } + if (!link.node_ops.readlink) { + throw new FS.ErrnoError(28); + } + return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link)); + }, + stat(path, dontFollow) { + var lookup = FS.lookupPath(path, { + follow: !dontFollow + }); + var node = lookup.node; + if (!node) { + throw new FS.ErrnoError(44); + } + if (!node.node_ops.getattr) { + throw new FS.ErrnoError(63); + } + return node.node_ops.getattr(node); + }, + lstat(path) { + return FS.stat(path, true); + }, + chmod(path, mode, dontFollow) { + var node; + if (typeof path == "string") { + var lookup = FS.lookupPath(path, { + follow: !dontFollow + }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + node.node_ops.setattr(node, { + mode: (mode & 4095) | (node.mode & ~4095), + timestamp: Date.now() + }); + }, + lchmod(path, mode) { + FS.chmod(path, mode, true); + }, + fchmod(fd, mode) { + var stream = FS.getStreamChecked(fd); + FS.chmod(stream.node, mode); + }, + chown(path, uid, gid, dontFollow) { + var node; + if (typeof path == "string") { + var lookup = FS.lookupPath(path, { + follow: !dontFollow + }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + node.node_ops.setattr(node, { + timestamp: Date.now() + }); + }, + // we ignore the uid / gid for now + lchown(path, uid, gid) { + FS.chown(path, uid, gid, true); + }, + fchown(fd, uid, gid) { + var stream = FS.getStreamChecked(fd); + FS.chown(stream.node, uid, gid); + }, + truncate(path, len) { + if (len < 0) { + throw new FS.ErrnoError(28); + } + var node; + if (typeof path == "string") { + var lookup = FS.lookupPath(path, { + follow: true + }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + if (FS.isDir(node.mode)) { + throw new FS.ErrnoError(31); + } + if (!FS.isFile(node.mode)) { + throw new FS.ErrnoError(28); + } + var errCode = FS.nodePermissions(node, "w"); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + node.node_ops.setattr(node, { + size: len, + timestamp: Date.now() + }); + }, + ftruncate(fd, len) { + var stream = FS.getStreamChecked(fd); + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(28); + } + FS.truncate(stream.node, len); + }, + utime(path, atime, mtime) { + var lookup = FS.lookupPath(path, { + follow: true + }); + var node = lookup.node; + node.node_ops.setattr(node, { + timestamp: Math.max(atime, mtime) + }); + }, + open(path, flags, mode) { + if (path === "") { + throw new FS.ErrnoError(44); + } + flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags; + if ((flags & 64)) { + mode = typeof mode == "undefined" ? 438 : /* 0666 */ mode; + mode = (mode & 4095) | 32768; + } else { + mode = 0; + } + var node; + if (typeof path == "object") { + node = path; + } else { + path = PATH.normalize(path); + try { + var lookup = FS.lookupPath(path, { + follow: !(flags & 131072) + }); + node = lookup.node; + } catch (e) {} + } + // perhaps we need to create the node + var created = false; + if ((flags & 64)) { + if (node) { + // if O_CREAT and O_EXCL are set, error out if the node already exists + if ((flags & 128)) { + throw new FS.ErrnoError(20); + } + } else { + // node doesn't exist, try to create it + node = FS.mknod(path, mode, 0); + created = true; + } + } + if (!node) { + throw new FS.ErrnoError(44); + } + // can't truncate a device + if (FS.isChrdev(node.mode)) { + flags &= ~512; + } + // if asked only for a directory, then this must be one + if ((flags & 65536) && !FS.isDir(node.mode)) { + throw new FS.ErrnoError(54); + } + // check permissions, if this is not a file we just created now (it is ok to + // create and write to a file with read-only permissions; it is read-only + // for later use) + if (!created) { + var errCode = FS.mayOpen(node, flags); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + } + // do truncation if necessary + if ((flags & 512) && !created) { + FS.truncate(node, 0); + } + // we've already handled these, don't pass down to the underlying vfs + flags &= ~(128 | 512 | 131072); + // register the stream with the filesystem + var stream = FS.createStream({ + node, + path: FS.getPath(node), + // we want the absolute path to the node + flags, + seekable: true, + position: 0, + stream_ops: node.stream_ops, + // used by the file family libc calls (fopen, fwrite, ferror, etc.) + ungotten: [], + error: false + }); + // call the new stream's open function + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + if (Module["logReadFiles"] && !(flags & 1)) { + if (!(path in FS.readFiles)) { + FS.readFiles[path] = 1; + } + } + return stream; + }, + close(stream) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (stream.getdents) stream.getdents = null; + // free readdir state + try { + if (stream.stream_ops.close) { + stream.stream_ops.close(stream); + } + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + stream.fd = null; + }, + isClosed(stream) { + return stream.fd === null; + }, + llseek(stream, offset, whence) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (!stream.seekable || !stream.stream_ops.llseek) { + throw new FS.ErrnoError(70); + } + if (whence != 0 && whence != 1 && whence != 2) { + throw new FS.ErrnoError(28); + } + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; + return stream.position; + }, + read(stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(28); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(8); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(31); + } + if (!stream.stream_ops.read) { + throw new FS.ErrnoError(28); + } + var seeking = typeof position != "undefined"; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError(70); + } + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write(stream, buffer, offset, length, position, canOwn) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(28); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(8); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(31); + } + if (!stream.stream_ops.write) { + throw new FS.ErrnoError(28); + } + if (stream.seekable && stream.flags & 1024) { + // seek to the end before writing in append mode + FS.llseek(stream, 0, 2); + } + var seeking = typeof position != "undefined"; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError(70); + } + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); + if (!seeking) stream.position += bytesWritten; + return bytesWritten; + }, + allocate(stream, offset, length) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (offset < 0 || length <= 0) { + throw new FS.ErrnoError(28); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(8); + } + if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + if (!stream.stream_ops.allocate) { + throw new FS.ErrnoError(138); + } + stream.stream_ops.allocate(stream, offset, length); + }, + mmap(stream, length, position, prot, flags) { + // User requests writing to file (prot & PROT_WRITE != 0). + // Checking if we have permissions to write to the file unless + // MAP_PRIVATE flag is set. According to POSIX spec it is possible + // to write to file opened in read-only mode with MAP_PRIVATE flag, + // as all modifications will be visible only in the memory of + // the current process. + if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) { + throw new FS.ErrnoError(2); + } + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(2); + } + if (!stream.stream_ops.mmap) { + throw new FS.ErrnoError(43); + } + if (!length) { + throw new FS.ErrnoError(28); + } + return stream.stream_ops.mmap(stream, length, position, prot, flags); + }, + msync(stream, buffer, offset, length, mmapFlags) { + if (!stream.stream_ops.msync) { + return 0; + } + return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); + }, + ioctl(stream, cmd, arg) { + if (!stream.stream_ops.ioctl) { + throw new FS.ErrnoError(59); + } + return stream.stream_ops.ioctl(stream, cmd, arg); + }, + readFile(path, opts = {}) { + opts.flags = opts.flags || 0; + opts.encoding = opts.encoding || "binary"; + if (opts.encoding !== "utf8" && opts.encoding !== "binary") { + throw new Error(`Invalid encoding type "${opts.encoding}"`); + } + var ret; + var stream = FS.open(path, opts.flags); + var stat = FS.stat(path); + var length = stat.size; + var buf = new Uint8Array(length); + FS.read(stream, buf, 0, length, 0); + if (opts.encoding === "utf8") { + ret = UTF8ArrayToString(buf); + } else if (opts.encoding === "binary") { + ret = buf; + } + FS.close(stream); + return ret; + }, + writeFile(path, data, opts = {}) { + opts.flags = opts.flags || 577; + var stream = FS.open(path, opts.flags, opts.mode); + if (typeof data == "string") { + var buf = new Uint8Array(lengthBytesUTF8(data) + 1); + var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length); + FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn); + } else if (ArrayBuffer.isView(data)) { + FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); + } else { + throw new Error("Unsupported data type"); + } + FS.close(stream); + }, + cwd: () => FS.currentPath, + chdir(path) { + var lookup = FS.lookupPath(path, { + follow: true + }); + if (lookup.node === null) { + throw new FS.ErrnoError(44); + } + if (!FS.isDir(lookup.node.mode)) { + throw new FS.ErrnoError(54); + } + var errCode = FS.nodePermissions(lookup.node, "x"); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + FS.currentPath = lookup.path; + }, + createDefaultDirectories() { + FS.mkdir("/tmp"); + FS.mkdir("/home"); + FS.mkdir("/home/web_user"); + }, + createDefaultDevices() { + // create /dev + FS.mkdir("/dev"); + // setup /dev/null + FS.registerDevice(FS.makedev(1, 3), { + read: () => 0, + write: (stream, buffer, offset, length, pos) => length + }); + FS.mkdev("/dev/null", FS.makedev(1, 3)); + // setup /dev/tty and /dev/tty1 + // stderr needs to print output using err() rather than out() + // so we register a second tty just for it. + TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); + TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); + FS.mkdev("/dev/tty", FS.makedev(5, 0)); + FS.mkdev("/dev/tty1", FS.makedev(6, 0)); + // setup /dev/[u]random + // use a buffer to avoid overhead of individual crypto calls per byte + var randomBuffer = new Uint8Array(1024), randomLeft = 0; + var randomByte = () => { + if (randomLeft === 0) { + randomLeft = randomFill(randomBuffer).byteLength; + } + return randomBuffer[--randomLeft]; + }; + FS.createDevice("/dev", "random", randomByte); + FS.createDevice("/dev", "urandom", randomByte); + // we're not going to emulate the actual shm device, + // just create the tmp dirs that reside in it commonly + FS.mkdir("/dev/shm"); + FS.mkdir("/dev/shm/tmp"); + }, + createSpecialDirectories() { + // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the + // name of the stream for fd 6 (see test_unistd_ttyname) + FS.mkdir("/proc"); + var proc_self = FS.mkdir("/proc/self"); + FS.mkdir("/proc/self/fd"); + FS.mount({ + mount() { + var node = FS.createNode(proc_self, "fd", 16384 | 511, /* 0777 */ 73); + node.node_ops = { + lookup(parent, name) { + var fd = +name; + var stream = FS.getStreamChecked(fd); + var ret = { + parent: null, + mount: { + mountpoint: "fake" + }, + node_ops: { + readlink: () => stream.path + } + }; + ret.parent = ret; + // make it look like a simple root node + return ret; + } + }; + return node; + } + }, {}, "/proc/self/fd"); + }, + createStandardStreams(input, output, error) { + // TODO deprecate the old functionality of a single + // input / output callback and that utilizes FS.createDevice + // and instead require a unique set of stream ops + // by default, we symlink the standard streams to the + // default tty devices. however, if the standard streams + // have been overwritten we create a unique device for + // them instead. + if (input) { + FS.createDevice("/dev", "stdin", input); + } else { + FS.symlink("/dev/tty", "/dev/stdin"); + } + if (output) { + FS.createDevice("/dev", "stdout", null, output); + } else { + FS.symlink("/dev/tty", "/dev/stdout"); + } + if (error) { + FS.createDevice("/dev", "stderr", null, error); + } else { + FS.symlink("/dev/tty1", "/dev/stderr"); + } + // open default streams for the stdin, stdout and stderr devices + var stdin = FS.open("/dev/stdin", 0); + var stdout = FS.open("/dev/stdout", 1); + var stderr = FS.open("/dev/stderr", 1); + }, + staticInit() { + // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info) + [ 44 ].forEach(code => { + FS.genericErrors[code] = new FS.ErrnoError(code); + FS.genericErrors[code].stack = ""; + }); + FS.nameTable = new Array(4096); + FS.mount(MEMFS, {}, "/"); + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + FS.filesystems = { + "MEMFS": MEMFS + }; + }, + init(input, output, error) { + FS.initialized = true; + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here + input ??= Module["stdin"]; + output ??= Module["stdout"]; + error ??= Module["stderr"]; + FS.createStandardStreams(input, output, error); + }, + quit() { + FS.initialized = false; + // force-flush all streams, so we get musl std streams printed out + // close all of our streams + for (var i = 0; i < FS.streams.length; i++) { + var stream = FS.streams[i]; + if (!stream) { + continue; + } + FS.close(stream); + } + }, + findObject(path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (!ret.exists) { + return null; + } + return ret.object; + }, + analyzePath(path, dontResolveLastLink) { + // operate from within the context of the symlink's target + try { + var lookup = FS.lookupPath(path, { + follow: !dontResolveLastLink + }); + path = lookup.path; + } catch (e) {} + var ret = { + isRoot: false, + exists: false, + error: 0, + name: null, + path: null, + object: null, + parentExists: false, + parentPath: null, + parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { + parent: true + }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { + follow: !dontResolveLastLink + }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === "/"; + } catch (e) { + ret.error = e.errno; + } + return ret; + }, + createPath(parent, path, canRead, canWrite) { + parent = typeof parent == "string" ? parent : FS.getPath(parent); + var parts = path.split("/").reverse(); + while (parts.length) { + var part = parts.pop(); + if (!part) continue; + var current = PATH.join2(parent, part); + try { + FS.mkdir(current); + } catch (e) {} + // ignore EEXIST + parent = current; + } + return current; + }, + createFile(parent, name, properties, canRead, canWrite) { + var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); + var mode = FS_getMode(canRead, canWrite); + return FS.create(path, mode); + }, + createDataFile(parent, name, data, canRead, canWrite, canOwn) { + var path = name; + if (parent) { + parent = typeof parent == "string" ? parent : FS.getPath(parent); + path = name ? PATH.join2(parent, name) : parent; + } + var mode = FS_getMode(canRead, canWrite); + var node = FS.create(path, mode); + if (data) { + if (typeof data == "string") { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); + data = arr; + } + // make sure we can write to the file + FS.chmod(node, mode | 146); + var stream = FS.open(node, 577); + FS.write(stream, data, 0, data.length, 0, canOwn); + FS.close(stream); + FS.chmod(node, mode); + } + }, + createDevice(parent, name, input, output) { + var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); + var mode = FS_getMode(!!input, !!output); + FS.createDevice.major ??= 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + // Create a fake device that a set of stream ops to emulate + // the old behavior. + FS.registerDevice(dev, { + open(stream) { + stream.seekable = false; + }, + close(stream) { + // flush any pending line data + if (output?.buffer?.length) { + output(10); + } + }, + read(stream, buffer, offset, length, pos) { + /* ignored */ var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(6); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) { + try { + output(buffer[offset + i]); + } catch (e) { + throw new FS.ErrnoError(29); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }); + return FS.mkdev(path, mode, dev); + }, + forceLoadFile(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + if (typeof XMLHttpRequest != "undefined") { + throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + } else { + // Command-line. + try { + obj.contents = readBinary(obj.url); + obj.usedBytes = obj.contents.length; + } catch (e) { + throw new FS.ErrnoError(29); + } + } + }, + createLazyFile(parent, name, url, canRead, canWrite) { + // Lazy chunked Uint8Array (implements get and length from Uint8Array). + // Actual getting is abstracted away for eventual reuse. + class LazyUint8Array { + constructor() { + this.lengthKnown = false; + this.chunks = []; + } + // Loaded chunks. Index is the chunk number + get(idx) { + if (idx > this.length - 1 || idx < 0) { + return undefined; + } + var chunkOffset = idx % this.chunkSize; + var chunkNum = (idx / this.chunkSize) | 0; + return this.getter(chunkNum)[chunkOffset]; + } + setDataGetter(getter) { + this.getter = getter; + } + cacheLength() { + // Find length + var xhr = new XMLHttpRequest; + xhr.open("HEAD", url, false); + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + var header; + var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; + var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; + var chunkSize = 1024 * 1024; + // Chunk size in bytes + if (!hasByteServing) chunkSize = datalength; + // Function to get a range from the remote URL. + var doXHR = (from, to) => { + if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength - 1) throw new Error("only " + datalength + " bytes available! programmer error!"); + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest; + xhr.open("GET", url, false); + if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + // Some hints to the browser that we want binary data. + xhr.responseType = "arraybuffer"; + if (xhr.overrideMimeType) { + xhr.overrideMimeType("text/plain; charset=x-user-defined"); + } + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(/** @type{Array} */ (xhr.response || [])); + } + return intArrayFromString(xhr.responseText || "", true); + }; + var lazyArray = this; + lazyArray.setDataGetter(chunkNum => { + var start = chunkNum * chunkSize; + var end = (chunkNum + 1) * chunkSize - 1; + // including this byte + end = Math.min(end, datalength - 1); + // if datalength-1 is selected, this is the last block + if (typeof lazyArray.chunks[chunkNum] == "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + } + if (typeof lazyArray.chunks[chunkNum] == "undefined") throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + if (usesGzip || !datalength) { + // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length + chunkSize = datalength = 1; + // this will force getter(0)/doXHR do download the whole file + datalength = this.getter(0).length; + chunkSize = datalength; + out("LazyFiles on gzip forces download of the whole file when length is accessed"); + } + this._length = datalength; + this._chunkSize = chunkSize; + this.lengthKnown = true; + } + get length() { + if (!this.lengthKnown) { + this.cacheLength(); + } + return this._length; + } + get chunkSize() { + if (!this.lengthKnown) { + this.cacheLength(); + } + return this._chunkSize; + } + } + if (typeof XMLHttpRequest != "undefined") { + if (!ENVIRONMENT_IS_WORKER) throw "Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc"; + var lazyArray = new LazyUint8Array; + var properties = { + isDevice: false, + contents: lazyArray + }; + } else { + var properties = { + isDevice: false, + url + }; + } + var node = FS.createFile(parent, name, properties, canRead, canWrite); + // This is a total hack, but I want to get this lazy file code out of the + // core of MEMFS. If we want to keep this lazy file concept I feel it should + // be its own thin LAZYFS proxying calls to MEMFS. + if (properties.contents) { + node.contents = properties.contents; + } else if (properties.url) { + node.contents = null; + node.url = properties.url; + } + // Add a function that defers querying the file size until it is asked the first time. + Object.defineProperties(node, { + usedBytes: { + get: function() { + return this.contents.length; + } + } + }); + // override each stream op with one that tries to force load the lazy file first + var stream_ops = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach(key => { + var fn = node.stream_ops[key]; + stream_ops[key] = (...args) => { + FS.forceLoadFile(node); + return fn(...args); + }; + }); + function writeChunks(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + if (position >= contents.length) return 0; + var size = Math.min(contents.length - position, length); + if (contents.slice) { + // normal array + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } else { + for (var i = 0; i < size; i++) { + // LazyUint8Array from sync binary XHR + buffer[offset + i] = contents.get(position + i); + } + } + return size; + } + // use a custom read function + stream_ops.read = (stream, buffer, offset, length, position) => { + FS.forceLoadFile(node); + return writeChunks(stream, buffer, offset, length, position); + }; + // use a custom mmap function + stream_ops.mmap = (stream, length, position, prot, flags) => { + FS.forceLoadFile(node); + var ptr = mmapAlloc(length); + if (!ptr) { + throw new FS.ErrnoError(48); + } + writeChunks(stream, HEAP8, ptr, length, position); + return { + ptr, + allocated: true + }; + }; + node.stream_ops = stream_ops; + return node; + } +}; + +/** + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index (i.e. maxBytesToRead will not + * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing + * frequent uses of UTF8ToString() with and without maxBytesToRead may throw + * JS JIT optimizations off, so it is worth to consider consistently using one + * @return {string} + */ var UTF8ToString = (ptr, maxBytesToRead) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; + +var SYSCALLS = { + DEFAULT_POLLMASK: 5, + calculateAt(dirfd, path, allowEmpty) { + if (PATH.isAbs(path)) { + return path; + } + // relative path + var dir; + if (dirfd === -100) { + dir = FS.cwd(); + } else { + var dirstream = SYSCALLS.getStreamFromFD(dirfd); + dir = dirstream.path; + } + if (path.length == 0) { + if (!allowEmpty) { + throw new FS.ErrnoError(44); + } + return dir; + } + return PATH.join2(dir, path); + }, + doStat(func, path, buf) { + var stat = func(path); + HEAP32[((buf) >> 2)] = stat.dev; + HEAP32[(((buf) + (4)) >> 2)] = stat.mode; + HEAPU32[(((buf) + (8)) >> 2)] = stat.nlink; + HEAP32[(((buf) + (12)) >> 2)] = stat.uid; + HEAP32[(((buf) + (16)) >> 2)] = stat.gid; + HEAP32[(((buf) + (20)) >> 2)] = stat.rdev; + (tempI64 = [ stat.size >>> 0, (tempDouble = stat.size, (+(Math.abs(tempDouble))) >= 1 ? (tempDouble > 0 ? (+(Math.floor((tempDouble) / 4294967296))) >>> 0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296))))) >>> 0) : 0) ], + HEAP32[(((buf) + (24)) >> 2)] = tempI64[0], HEAP32[(((buf) + (28)) >> 2)] = tempI64[1]); + HEAP32[(((buf) + (32)) >> 2)] = 4096; + HEAP32[(((buf) + (36)) >> 2)] = stat.blocks; + var atime = stat.atime.getTime(); + var mtime = stat.mtime.getTime(); + var ctime = stat.ctime.getTime(); + (tempI64 = [ Math.floor(atime / 1e3) >>> 0, (tempDouble = Math.floor(atime / 1e3), + (+(Math.abs(tempDouble))) >= 1 ? (tempDouble > 0 ? (+(Math.floor((tempDouble) / 4294967296))) >>> 0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296))))) >>> 0) : 0) ], + HEAP32[(((buf) + (40)) >> 2)] = tempI64[0], HEAP32[(((buf) + (44)) >> 2)] = tempI64[1]); + HEAPU32[(((buf) + (48)) >> 2)] = (atime % 1e3) * 1e3 * 1e3; + (tempI64 = [ Math.floor(mtime / 1e3) >>> 0, (tempDouble = Math.floor(mtime / 1e3), + (+(Math.abs(tempDouble))) >= 1 ? (tempDouble > 0 ? (+(Math.floor((tempDouble) / 4294967296))) >>> 0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296))))) >>> 0) : 0) ], + HEAP32[(((buf) + (56)) >> 2)] = tempI64[0], HEAP32[(((buf) + (60)) >> 2)] = tempI64[1]); + HEAPU32[(((buf) + (64)) >> 2)] = (mtime % 1e3) * 1e3 * 1e3; + (tempI64 = [ Math.floor(ctime / 1e3) >>> 0, (tempDouble = Math.floor(ctime / 1e3), + (+(Math.abs(tempDouble))) >= 1 ? (tempDouble > 0 ? (+(Math.floor((tempDouble) / 4294967296))) >>> 0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296))))) >>> 0) : 0) ], + HEAP32[(((buf) + (72)) >> 2)] = tempI64[0], HEAP32[(((buf) + (76)) >> 2)] = tempI64[1]); + HEAPU32[(((buf) + (80)) >> 2)] = (ctime % 1e3) * 1e3 * 1e3; + (tempI64 = [ stat.ino >>> 0, (tempDouble = stat.ino, (+(Math.abs(tempDouble))) >= 1 ? (tempDouble > 0 ? (+(Math.floor((tempDouble) / 4294967296))) >>> 0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296))))) >>> 0) : 0) ], + HEAP32[(((buf) + (88)) >> 2)] = tempI64[0], HEAP32[(((buf) + (92)) >> 2)] = tempI64[1]); + return 0; + }, + doMsync(addr, stream, len, flags, offset) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + if (flags & 2) { + // MAP_PRIVATE calls need not to be synced back to underlying fs + return 0; + } + var buffer = HEAPU8.slice(addr, addr + len); + FS.msync(stream, buffer, offset, len, flags); + }, + getStreamFromFD(fd) { + var stream = FS.getStreamChecked(fd); + return stream; + }, + varargs: undefined, + getStr(ptr) { + var ret = UTF8ToString(ptr); + return ret; + } +}; + +function ___syscall_fcntl64(fd, cmd, varargs) { + SYSCALLS.varargs = varargs; + try { + var stream = SYSCALLS.getStreamFromFD(fd); + switch (cmd) { + case 0: + { + var arg = syscallGetVarargI(); + if (arg < 0) { + return -28; + } + while (FS.streams[arg]) { + arg++; + } + var newStream; + newStream = FS.dupStream(stream, arg); + return newStream.fd; + } + + case 1: + case 2: + return 0; + + // FD_CLOEXEC makes no sense for a single process. + case 3: + return stream.flags; + + case 4: + { + var arg = syscallGetVarargI(); + stream.flags |= arg; + return 0; + } + + case 12: + { + var arg = syscallGetVarargP(); + var offset = 0; + // We're always unlocked. + HEAP16[(((arg) + (offset)) >> 1)] = 2; + return 0; + } + + case 13: + case 14: + return 0; + } + // Pretend that the locking is successful. + return -28; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } +} + +function ___syscall_ioctl(fd, op, varargs) { + SYSCALLS.varargs = varargs; + try { + var stream = SYSCALLS.getStreamFromFD(fd); + switch (op) { + case 21509: + { + if (!stream.tty) return -59; + return 0; + } + + case 21505: + { + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcgets) { + var termios = stream.tty.ops.ioctl_tcgets(stream); + var argp = syscallGetVarargP(); + HEAP32[((argp) >> 2)] = termios.c_iflag || 0; + HEAP32[(((argp) + (4)) >> 2)] = termios.c_oflag || 0; + HEAP32[(((argp) + (8)) >> 2)] = termios.c_cflag || 0; + HEAP32[(((argp) + (12)) >> 2)] = termios.c_lflag || 0; + for (var i = 0; i < 32; i++) { + HEAP8[(argp + i) + (17)] = termios.c_cc[i] || 0; + } + return 0; + } + return 0; + } + + case 21510: + case 21511: + case 21512: + { + if (!stream.tty) return -59; + return 0; + } + + // no-op, not actually adjusting terminal settings + case 21506: + case 21507: + case 21508: + { + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcsets) { + var argp = syscallGetVarargP(); + var c_iflag = HEAP32[((argp) >> 2)]; + var c_oflag = HEAP32[(((argp) + (4)) >> 2)]; + var c_cflag = HEAP32[(((argp) + (8)) >> 2)]; + var c_lflag = HEAP32[(((argp) + (12)) >> 2)]; + var c_cc = []; + for (var i = 0; i < 32; i++) { + c_cc.push(HEAP8[(argp + i) + (17)]); + } + return stream.tty.ops.ioctl_tcsets(stream.tty, op, { + c_iflag, + c_oflag, + c_cflag, + c_lflag, + c_cc + }); + } + return 0; + } + + // no-op, not actually adjusting terminal settings + case 21519: + { + if (!stream.tty) return -59; + var argp = syscallGetVarargP(); + HEAP32[((argp) >> 2)] = 0; + return 0; + } + + case 21520: + { + if (!stream.tty) return -59; + return -28; + } + + // not supported + case 21531: + { + var argp = syscallGetVarargP(); + return FS.ioctl(stream, op, argp); + } + + case 21523: + { + // TODO: in theory we should write to the winsize struct that gets + // passed in, but for now musl doesn't read anything on it + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tiocgwinsz) { + var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty); + var argp = syscallGetVarargP(); + HEAP16[((argp) >> 1)] = winsize[0]; + HEAP16[(((argp) + (2)) >> 1)] = winsize[1]; + } + return 0; + } + + case 21524: + { + // TODO: technically, this ioctl call should change the window size. + // but, since emscripten doesn't have any concept of a terminal window + // yet, we'll just silently throw it away as we do TIOCGWINSZ + if (!stream.tty) return -59; + return 0; + } + + case 21515: + { + if (!stream.tty) return -59; + return 0; + } + + default: + return -28; + } + } // not supported + catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } +} + +function ___syscall_openat(dirfd, path, flags, varargs) { + SYSCALLS.varargs = varargs; + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + var mode = varargs ? syscallGetVarargI() : 0; + return FS.open(path, flags, mode).fd; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } +} + +function ___syscall_unlinkat(dirfd, path, flags) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (flags === 0) { + FS.unlink(path); + } else if (flags === 512) { + FS.rmdir(path); + } else { + abort("Invalid flags passed to unlinkat"); + } + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } +} + +var __abort_js = () => { + abort(""); +}; + +var __emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num); + +var _emscripten_date_now = () => Date.now(); + +var abortOnCannotGrowMemory = requestedSize => { + abort("OOM"); +}; + +var _emscripten_resize_heap = requestedSize => { + var oldSize = HEAPU8.length; + // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. + requestedSize >>>= 0; + abortOnCannotGrowMemory(requestedSize); +}; + +var ENV = {}; + +var getExecutableName = () => thisProgram || "./this.program"; + +var getEnvStrings = () => { + if (!getEnvStrings.strings) { + // Default values. + // Browser language detection #8751 + var lang = ((typeof navigator == "object" && navigator.languages && navigator.languages[0]) || "C").replace("-", "_") + ".UTF-8"; + var env = { + "USER": "web_user", + "LOGNAME": "web_user", + "PATH": "/", + "PWD": "/", + "HOME": "/home/web_user", + "LANG": lang, + "_": getExecutableName() + }; + // Apply the user-provided values, if any. + for (var x in ENV) { + // x is a key in ENV; if ENV[x] is undefined, that means it was + // explicitly set to be so. We allow user code to do that to + // force variables with default values to remain unset. + if (ENV[x] === undefined) delete env[x]; else env[x] = ENV[x]; + } + var strings = []; + for (var x in env) { + strings.push(`${x}=${env[x]}`); + } + getEnvStrings.strings = strings; + } + return getEnvStrings.strings; +}; + +var stringToAscii = (str, buffer) => { + for (var i = 0; i < str.length; ++i) { + HEAP8[buffer++] = str.charCodeAt(i); + } + // Null-terminate the string + HEAP8[buffer] = 0; +}; + +var _environ_get = (__environ, environ_buf) => { + var bufSize = 0; + getEnvStrings().forEach((string, i) => { + var ptr = environ_buf + bufSize; + HEAPU32[(((__environ) + (i * 4)) >> 2)] = ptr; + stringToAscii(string, ptr); + bufSize += string.length + 1; + }); + return 0; +}; + +var _environ_sizes_get = (penviron_count, penviron_buf_size) => { + var strings = getEnvStrings(); + HEAPU32[((penviron_count) >> 2)] = strings.length; + var bufSize = 0; + strings.forEach(string => bufSize += string.length + 1); + HEAPU32[((penviron_buf_size) >> 2)] = bufSize; + return 0; +}; + +var runtimeKeepaliveCounter = 0; + +var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; + +var _proc_exit = code => { + EXITSTATUS = code; + if (!keepRuntimeAlive()) { + Module["onExit"]?.(code); + ABORT = true; + } + quit_(code, new ExitStatus(code)); +}; + +/** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { + EXITSTATUS = status; + _proc_exit(status); +}; + +var _exit = exitJS; + +function _fd_close(fd) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + FS.close(stream); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } +} + +/** @param {number=} offset */ var doReadv = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[((iov) >> 2)]; + var len = HEAPU32[(((iov) + (4)) >> 2)]; + iov += 8; + var curr = FS.read(stream, HEAP8, ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + if (curr < len) break; + // nothing more to read + if (typeof offset != "undefined") { + offset += curr; + } + } + return ret; +}; + +function _fd_read(fd, iov, iovcnt, pnum) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var num = doReadv(stream, iov, iovcnt); + HEAPU32[((pnum) >> 2)] = num; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } +} + +var convertI32PairToI53Checked = (lo, hi) => ((hi + 2097152) >>> 0 < 4194305 - !!lo) ? (lo >>> 0) + hi * 4294967296 : NaN; + +function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { + var offset = convertI32PairToI53Checked(offset_low, offset_high); + try { + if (isNaN(offset)) return 61; + var stream = SYSCALLS.getStreamFromFD(fd); + FS.llseek(stream, offset, whence); + (tempI64 = [ stream.position >>> 0, (tempDouble = stream.position, (+(Math.abs(tempDouble))) >= 1 ? (tempDouble > 0 ? (+(Math.floor((tempDouble) / 4294967296))) >>> 0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296))))) >>> 0) : 0) ], + HEAP32[((newOffset) >> 2)] = tempI64[0], HEAP32[(((newOffset) + (4)) >> 2)] = tempI64[1]); + if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; + // reset readdir state + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } +} + +/** @param {number=} offset */ var doWritev = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[((iov) >> 2)]; + var len = HEAPU32[(((iov) + (4)) >> 2)]; + iov += 8; + var curr = FS.write(stream, HEAP8, ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + if (curr < len) { + // No more space to write. + break; + } + if (typeof offset != "undefined") { + offset += curr; + } + } + return ret; +}; + +function _fd_write(fd, iov, iovcnt, pnum) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var num = doWritev(stream, iov, iovcnt); + HEAPU32[((pnum) >> 2)] = num; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } +} + +var handleException = e => { + // Certain exception types we do not treat as errors since they are used for + // internal control flow. + // 1. ExitStatus, which is thrown by exit() + // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others + // that wish to return to JS event loop. + if (e instanceof ExitStatus || e == "unwind") { + return EXITSTATUS; + } + quit_(1, e); +}; + +var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); + +var stackAlloc = sz => __emscripten_stack_alloc(sz); + +var stringToUTF8OnStack = str => { + var size = lengthBytesUTF8(str) + 1; + var ret = stackAlloc(size); + stringToUTF8(str, ret, size); + return ret; +}; + +FS.createPreloadedFile = FS_createPreloadedFile; + +FS.staticInit(); + +var wasmImports = { + /** @export */ __syscall_fcntl64: ___syscall_fcntl64, + /** @export */ __syscall_ioctl: ___syscall_ioctl, + /** @export */ __syscall_openat: ___syscall_openat, + /** @export */ __syscall_unlinkat: ___syscall_unlinkat, + /** @export */ _abort_js: __abort_js, + /** @export */ _emscripten_memcpy_js: __emscripten_memcpy_js, + /** @export */ emscripten_date_now: _emscripten_date_now, + /** @export */ emscripten_resize_heap: _emscripten_resize_heap, + /** @export */ environ_get: _environ_get, + /** @export */ environ_sizes_get: _environ_sizes_get, + /** @export */ exit: _exit, + /** @export */ fd_close: _fd_close, + /** @export */ fd_read: _fd_read, + /** @export */ fd_seek: _fd_seek, + /** @export */ fd_write: _fd_write +}; + +var wasmExports = createWasm(); + +var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports["__wasm_call_ctors"])(); + +var _main = Module["_main"] = (a0, a1) => (_main = Module["_main"] = wasmExports["__main_argc_argv"])(a0, a1); + +var _runIteration = Module["_runIteration"] = () => (_runIteration = Module["_runIteration"] = wasmExports["runIteration"])(); + +var __emscripten_stack_restore = a0 => (__emscripten_stack_restore = wasmExports["_emscripten_stack_restore"])(a0); + +var __emscripten_stack_alloc = a0 => (__emscripten_stack_alloc = wasmExports["_emscripten_stack_alloc"])(a0); + +var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports["emscripten_stack_get_current"])(); + +var dynCall_jiji = Module["dynCall_jiji"] = (a0, a1, a2, a3, a4) => (dynCall_jiji = Module["dynCall_jiji"] = wasmExports["dynCall_jiji"])(a0, a1, a2, a3, a4); + +// include: postamble.js +// === Auto-generated postamble setup entry stuff === +var calledRun; + +var calledPrerun; + +dependenciesFulfilled = function runCaller() { + // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; +}; + +// try this again later, after new deps are fulfilled +function callMain(args = []) { + var entryFunction = _main; + args.unshift(thisProgram); + var argc = args.length; + var argv = stackAlloc((argc + 1) * 4); + var argv_ptr = argv; + args.forEach(arg => { + HEAPU32[((argv_ptr) >> 2)] = stringToUTF8OnStack(arg); + argv_ptr += 4; + }); + HEAPU32[((argv_ptr) >> 2)] = 0; + try { + var ret = entryFunction(argc, argv); + // if we're not running an evented main loop, it's time to exit + exitJS(ret, /* implicit = */ true); + return ret; + } catch (e) { + return handleException(e); + } +} + +function run(args = arguments_) { + if (runDependencies > 0) { + return; + } + if (!calledPrerun) { + calledPrerun = 1; + preRun(); + // a preRun added a dependency, run will be called later + if (runDependencies > 0) { + return; + } + } + function doRun() { + // run may have just been called through dependencies being fulfilled just in this very frame, + // or while the async setStatus time below was happening + if (calledRun) return; + calledRun = 1; + Module["calledRun"] = 1; + if (ABORT) return; + initRuntime(); + preMain(); + Module["onRuntimeInitialized"]?.(); + if (shouldRunNow) callMain(args); + postRun(); + } + if (Module["setStatus"]) { + Module["setStatus"]("Running..."); + setTimeout(() => { + setTimeout(() => Module["setStatus"](""), 1); + doRun(); + }, 1); + } else { + doRun(); + } +} + +if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ]; + while (Module["preInit"].length > 0) { + Module["preInit"].pop()(); + } +} + +// shouldRunNow refers to calling main(), not run(). +var shouldRunNow = true; + +if (Module["noInitialRun"]) shouldRunNow = false; + +run(); +} + +class Benchmark { + async runIteration() { + if (!Module["_main"]) { + let runtimeInitializedCallback; + let runtimeInitialized = new Promise((success) => runtimeInitializedCallback = success); + Module.onRuntimeInitialized = function() { + runtimeInitializedCallback(); + } + setup(Module); + await runtimeInitialized; + } + + Module["_runIteration"](); + } +} diff --git a/wasm/TSF/tsf.js.symbols b/wasm/TSF/tsf.js.symbols new file mode 100644 index 0000000..cb9fd46 --- /dev/null +++ b/wasm/TSF/tsf.js.symbols @@ -0,0 +1,260 @@ +0:exit +1:__syscall_fcntl64 +2:__syscall_ioctl +3:__wasi_fd_close +4:__wasi_fd_read +5:__wasi_fd_write +6:_abort_js +7:_emscripten_memcpy_js +8:emscripten_date_now +9:__syscall_openat +10:__wasi_environ_sizes_get +11:__wasi_environ_get +12:__syscall_unlinkat +13:emscripten_resize_heap +14:legalimport$__wasi_fd_seek +15:__wasm_call_ctors +16:tsf_vasprintf +17:tsf_asprintf +18:tsf_buffer_initialize_custom +19:tsf_buffer_destroy_custom +20:read_buffer_impl +21:write_buffer_impl +22:tsf_set_error_fullv +23:tsf_set_error_full +24:tsf_set_error +25:tsf_set_errno +26:new_size +27:tsf_st_init_ptrtable +28:tsf_st_init_strtable +29:tsf_st_free_table +30:tsf_st_lookup +31:tsf_st_insert +32:tsf_st_add_direct +33:tsf_st_delete +34:tsf_st_foreach +35:tsf_st_strhash +36:ptrcmp +37:tsf_st_ptrhash +38:tsf_type_create +39:check_init_types +40:tsf_type_recompute_hash +41:tsf_type_destroy +42:tsf_struct_type_append +43:tsf_type_clone +44:tsf_choice_type_append +45:tsf_type_read_rec +46:tsf_choice_type_get_num_elements +47:tsf_type_write +48:tsf_type_dup +49:tsf_type_own_begin +50:tsf_type_own_commit +51:tsf_type_get_byte_size +52:tsf_type_get_hash +53:tsf_type_compare +54:tsf_type_memo +55:tsf_type_instanceof +56:tsf_struct_type_get_num_elements +57:tsf_struct_type_get_element +58:tsf_struct_type_find_node +59:tsf_choice_type_get_element +60:tsf_choice_type_find_node +61:tsf_type_get_static_size +62:tsf_choice_type_has_non_void +63:tsf_fd_writer +64:tsf_fd_partial_reader +65:tsf_size_calc_writer +66:tsf_size_aware_reader +67:tsf_native_type_has_struct_mapping +68:tsf_native_type_get_size +69:tsf_native_struct_type_map +70:tsf_native_struct_type_set_size +71:tsf_type_compare_adaptor +72:tsf_parser_create +73:tsf_parser_parse_into +74:tsf_buf_writer_destroy +75:tsf_buf_writer_write +76:tsf_buf_reader_read +77:tsf_type_table_create +78:tsf_type_table_copy +79:tsf_type_table_append +80:tsf_type_table_find_by_index +81:tsf_type_table_destroy +82:tsf_type_table_read +83:tsf_type_table_write +84:tsf_type_table_get_hash +85:tsf_type_table_contains_dynamic +86:tsf_type_table_compare +87:tsf_type_table_find_by_name +88:generate_size_calc +89:generate_generator +90:generate_parser +91:generate_set_default +92:gpc_code_gen_debug +93:gpc_threaded_destroy +94:gpc_threaded_run +95:back_branch_action +96:fore_branch_action +97:st_aa_free +98:correct_branch +99:gpc_intable_destroy +100:gpc_intable_run +101:gpc_instruction_static_size +102:gpc_instruction_size +103:gpc_instruction_for_all_branches +104:gpc_program_from_proto +105:gpc_program_run +106:gpc_proto_create +107:gpc_proto_destroy +108:gpc_proto_append +109:gpc_proto_append_tablejump_local +110:gpc_proto_append_tablejump_field +111:gpc_intable_get_stack_heights +112:branch_action +113:tsf_type_in_map_create +114:tsf_type_in_map_from_type_out_map_in +115:table_snatch_pair +116:tsf_type_in_map_get_empty_singleton +117:tsf_type_in_map_destroy +118:tsf_type_in_map_prepare +119:tsf_type_in_map_append +120:tsf_type_in_map_get_type +121:tsf_type_out_map_create +122:tsf_type_out_map_destroy +123:table_destroy_pair +124:tsf_type_out_map_get_type_code +125:tsf_stream_file_input_open +126:tsf_stream_file_input_close +127:tsf_stream_file_input_read_existing_buffer +128:tsf_named_type_destroy +129:tsf_named_type_get_hash +130:tsf_read_string +131:tsf_write_string +132:tsf_full_read_of_partial +133:tsf_zip_wtr_attr_get_default +134:tsf_zip_rdr_attr_get_default +135:tsf_zip_writer_destroy +136:tsf_zip_writer_write +137:tsf_zip_abstract_deflate +138:tsf_adaptive_reader_destroy +139:tear_down_mode +140:tsf_adaptive_reader_read +141:select_mode +142:tsf_type_create_aoe +143:aoe_failure +144:tsf_type_create_array_aoe +145:tsf_type_create_struct_with_native_size_aoe +146:tsf_type_create_struct_with_native_size_and_destructor_aoe +147:tsf_type_create_choice_with_native_map_aoe +148:tsf_struct_type_append_primitive_with_native_map_aoe +149:tsf_struct_type_append_from_callback_and_dup_with_native_map_aoe +150:tsf_choice_type_append_primitive_aoe +151:tsf_choice_type_append_from_callback_and_dup_aoe +152:tsf_type_set_name_aoe +153:tsf_typed_data_read +154:Operand__get_type +155:Type__get_type +156:CodeOffset__get_type +157:VariableDecl__destruct +158:Instruction__alloc__destruct +159:Instruction__call__destruct +160:ProcedureDecl__get_type +161:ProcedureDefn__get_type +162:ProcedureDefn__destruct +163:Program__get_type +164:Program__get_parser +165:Program__destruct +166:Program__read +167:Program__read_into +168:Instruction__mov__get_type +169:Instruction__add__get_type +170:Instruction__alloc__get_type +171:Instruction__ret__get_type +172:Instruction__jump__get_type +173:Instruction__call__get_type +174:Instruction__call__args__get_type +175:Instruction__branchZero__get_type +176:ProcedureDefn__variables__get_type +177:ProcedureDefn__code__get_type +178:ProcedureDefn__debug__get_type +179:Program__globals__get_type +180:DOperand__get_type +181:DType__get_type +182:DCodeOffset__get_type +183:DInstruction__call__destruct +184:DProcedureDecl__get_type +185:DProcedureDefn__get_type +186:DProcedureDefn__destruct +187:DProgram__destruct +188:DProgram__read +189:DInstruction__mov__get_type +190:DInstruction__add__get_type +191:DInstruction__alloc__get_type +192:DInstruction__ret__get_type +193:DInstruction__jump__get_type +194:DInstruction__call__get_type +195:DInstruction__call__args__get_type +196:DInstruction__branchZero__get_type +197:DProcedureDefn__variables__get_type +198:DProcedureDefn__code__get_type +199:DProgram__globals__get_type +200:main +201:runIteration +202:__stdio_close +203:__stdio_read +204:__stdio_seek +205:__stdio_write +206:abort +207:close +208:__memcpy +209:__memset +210:__gettimeofday +211:fflush +212:fiprintf +213:__towrite +214:__overflow +215:fputc +216:__fwritex +217:getenv +218:__bswap_32 +219:__lseek +220:open +221:__small_printf +222:read +223:snprintf +224:__emscripten_stdout_close +225:__emscripten_stdout_seek +226:__stpcpy +227:strchr +228:__strchrnul +229:strcmp +230:strdup +231:__strerror_l +232:strlen +233:__syscall_ret +234:tolower +235:vasprintf +236:frexp +237:__vfprintf_internal +238:printf_core +239:out +240:getint +241:pop_arg +242:fmt_u +243:pad +244:fmt_fp +245:pop_arg_long_double +246:vsnprintf +247:sn_write +248:__wasi_syscall_ret +249:wctomb +250:sbrk +251:emscripten_builtin_malloc +252:emscripten_builtin_free +253:dlrealloc +254:dispose_chunk +255:emscripten_builtin_calloc +256:_emscripten_stack_restore +257:_emscripten_stack_alloc +258:emscripten_stack_get_current +259:legalstub$dynCall_jiji diff --git a/wasm/TSF/tsf.wasm b/wasm/TSF/tsf.wasm new file mode 100755 index 0000000000000000000000000000000000000000..d2022f65710c2d4bb412a8ba6012b1edf2f31e7f GIT binary patch literal 144449 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 NA+QG78o>j$tnAVy; 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 qlJPJH*Ju6{K*zlCzz(> Date: Tue, 17 Dec 2024 12:06:34 -0500 Subject: [PATCH 2/4] Use Modularize instead of wrapping emcc's output. This seems to work pretty nicely and should be future proof. --- JetStreamDriver.js | 34 +++++-- wasm/TSF/build.sh | 22 +--- wasm/TSF/tsf.js | 219 +++++++++++++++++++++++----------------- wasm/TSF/tsf.js.symbols | 12 +-- wasm/TSF/tsf.wasm | Bin 144449 -> 144423 bytes wasm/TSF/tsf_ir_speed.c | 2 +- 6 files changed, 160 insertions(+), 129 deletions(-) diff --git a/JetStreamDriver.js b/JetStreamDriver.js index 698cf5a..f72c797 100644 --- a/JetStreamDriver.js +++ b/JetStreamDriver.js @@ -36,11 +36,17 @@ globalThis.dumpJSONResults ??= false; globalThis.customTestList ??= []; let shouldReport = false; +let startDelay; if (typeof(URLSearchParams) !== "undefined") { const urlParameters = new URLSearchParams(window.location.search); shouldReport = urlParameters.has('report') && urlParameters.get('report').toLowerCase() == 'true'; + if (shouldReport) + startDelay = 4000; + if (urlParameters.has('startDelay')) + startDelay = urlParameters.get('startDelay'); if (urlParameters.has('test')) customTestList = urlParameters.getAll("test"); + } // Used for the promise representing the current benchmark run. @@ -414,8 +420,8 @@ class Driver { await this.prefetchResourcesForBrowser(); await this.fetchResources(); this.prepareToRun(); - if (isInBrowser && shouldReport) { - setTimeout(() => this.start(), 4000); + if (isInBrowser && startDelay !== undefined) { + setTimeout(() => this.start(), startDelay); } } @@ -991,6 +997,7 @@ class AsyncBenchmark extends DefaultBenchmark { await __benchmark.runIteration(); let end = performance.now(); ${this.postIterationCode} + console.log("iteration i: " + (end - start)); results.push(Math.max(1, end - start)); } if (__benchmark.validate) @@ -1001,7 +1008,10 @@ class AsyncBenchmark extends DefaultBenchmark { } }; -class WasmBenchmark extends AsyncBenchmark { +// Meant for wasm benchmarks that are directly compiled with an emcc script and not part of a larger project's build system. +// Requires the following flags to emcc `-s MODULARIZE=1 -s EXPORT_NAME=setupModule -s EXPORTED_FUNCTIONS=_runIteration` +// in addition to any other benchmark specific flags. +class WasmEMCCBenchmark extends AsyncBenchmark { get prerunCode() { let str = ` let verbose = true; @@ -1019,11 +1029,6 @@ class WasmBenchmark extends AsyncBenchmark { console.log('Intercepted print: ', ...args); }; - let instanceReady; - let instancePromise = new Promise((resolve) => { - instanceReady = resolve; - }); - let Module = { preRun: [], postRun: [], @@ -1081,6 +1086,17 @@ class WasmBenchmark extends AsyncBenchmark { for (let i = 0; i < keys.length; ++i) { str += `loadBlob("${keys[i]}", "${this.plan.preload[keys[i]]}", () => {\n`; } + str += ` + class Benchmark { + async runIteration() { + if (!Module["_runIteration"]) + await setupModule(Module); + + Module["_runIteration"](); + } + }; + ` + str += super.runnerCode; for (let i = 0; i < keys.length; ++i) { str += `})`; @@ -1892,7 +1908,7 @@ const testPlans = [ preload: { wasmBinary: "./wasm/TSF/tsf.wasm" }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmEMCCBenchmark, testGroup: WasmGroup }, { diff --git a/wasm/TSF/build.sh b/wasm/TSF/build.sh index ef51acf..93c526d 100755 --- a/wasm/TSF/build.sh +++ b/wasm/TSF/build.sh @@ -3,7 +3,7 @@ set -euo pipefail emcc \ - -o tsf.js -O2 -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 --emit-symbol-map -s EXPORTED_FUNCTIONS=_main,_runIteration \ + -o tsf.js -O2 -s MODULARIZE=1 -s EXPORT_NAME=setupModule -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 --emit-symbol-map -s EXPORTED_FUNCTIONS=_main,_runIteration \ -I. -DTSF_BUILD_SYSTEM=1 \ tsf_asprintf.c\ tsf_buffer.c\ @@ -56,23 +56,3 @@ emcc \ tsf_ir_different.c\ tsf_ir_speed.c -TEMPFILE=`mktemp /tmp/tsf.XXXXXX` - -(echo 'function setup(Module) {'; cat tsf.js; echo '} - -class Benchmark { - async runIteration() { - if (!Module["_main"]) { - let runtimeInitializedCallback; - let runtimeInitialized = new Promise((success) => runtimeInitializedCallback = success); - Module.onRuntimeInitialized = function() { - runtimeInitializedCallback(); - } - setup(Module); - await runtimeInitialized; - } - - Module["_runIteration"](); - } -}') > $TEMPFILE -mv $TEMPFILE tsf.js diff --git a/wasm/TSF/tsf.js b/wasm/TSF/tsf.js index b463a59..fe388b8 100644 --- a/wasm/TSF/tsf.js +++ b/wasm/TSF/tsf.js @@ -1,4 +1,11 @@ -function setup(Module) { + +var setupModule = (() => { + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; + if (typeof __filename != 'undefined') _scriptName = _scriptName || __filename; + return ( +function(moduleArg = {}) { + var moduleRtn; + // include: shell.js // The Module object: Our interface to the outside world. We import // and export values on it. There are various ways Module can be used: @@ -13,14 +20,22 @@ function setup(Module) { // after the generated code, you will need to define var Module = {}; // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. -var Module = typeof Module != "undefined" ? Module : {}; +var Module = moduleArg; + +// Set up the promise that indicates the Module is initialized +var readyPromiseResolve, readyPromiseReject; + +var readyPromise = new Promise((resolve, reject) => { + readyPromiseResolve = resolve; + readyPromiseReject = reject; +}); // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). // Attempt to auto-detect the environment var ENVIRONMENT_IS_WEB = typeof window == "object"; -var ENVIRONMENT_IS_WORKER = typeof importScripts == "function"; +var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != "undefined"; // N.b. Electron.js environment is simultaneously a NODE-environment, but // also a web environment. @@ -86,9 +101,7 @@ if (ENVIRONMENT_IS_NODE) { thisProgram = process.argv[1].replace(/\\/g, "/"); } arguments_ = process.argv.slice(2); - if (typeof module != "undefined") { - module["exports"] = Module; - } + // MODULARIZE will export the module in the proper place outside, we don't need to export here quit_ = (status, toThrow) => { process.exitCode = status; throw toThrow; @@ -104,6 +117,11 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { // web scriptDirectory = document.currentScript.src; } + // When MODULARIZE, this JS may be executed later, after document.currentScript + // is gone, so we saved it, and we use it here instead of any other info. + if (_scriptName) { + scriptDirectory = _scriptName; + } // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. // otherwise, slice off the final part of the url to find the script directory. // if scriptDirectory does not contain a slash, lastIndexOf will return -1, @@ -242,10 +260,11 @@ var __ATPOSTRUN__ = []; var runtimeInitialized = false; function preRun() { - var preRuns = Module["preRun"]; - if (preRuns) { - if (typeof preRuns == "function") preRuns = [ preRuns ]; - preRuns.forEach(addOnPreRun); + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") Module["preRun"] = [ Module["preRun"] ]; + while (Module["preRun"].length) { + addOnPreRun(Module["preRun"].shift()); + } } callRuntimeCallbacks(__ATPRERUN__); } @@ -263,10 +282,11 @@ function preMain() { } function postRun() { - var postRuns = Module["postRun"]; - if (postRuns) { - if (typeof postRuns == "function") postRuns = [ postRuns ]; - postRuns.forEach(addOnPostRun); + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") Module["postRun"] = [ Module["postRun"] ]; + while (Module["postRun"].length) { + addOnPostRun(Module["postRun"].shift()); + } } callRuntimeCallbacks(__ATPOSTRUN__); } @@ -349,6 +369,7 @@ function removeRunDependency(id) { // though it can. // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); + readyPromiseReject(e); // Throw the error whether or not MODULARIZE is set because abort is used // in code paths apart from instantiation where an exception is expected // to be thrown when abort is called. @@ -451,7 +472,6 @@ function getWasmImports() { // Create the wasm instance. // Receives the wasm imports, returns the exports. function createWasm() { - var info = getWasmImports(); // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup @@ -473,6 +493,7 @@ function createWasm() { // When the regression is fixed, can restore the above PTHREADS-enabled path. receiveInstance(result["instance"]); } + var info = getWasmImports(); // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback // to manually instantiate the Wasm module themselves. This allows pages to // run the instantiation parallel to any other async startup actions they are @@ -484,11 +505,13 @@ function createWasm() { return Module["instantiateWasm"](info, receiveInstance); } catch (e) { err(`Module.instantiateWasm callback failed with error: ${e}`); - return false; + // If instantiation fails, reject the module ready promise. + readyPromiseReject(e); } } wasmBinaryFile ??= findWasmBinary(); - instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult); + // If instantiation fails, reject the module ready promise. + instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject); return {}; } @@ -501,25 +524,29 @@ var tempI64; // end include: runtime_debug.js // === Body === // end include: preamble.js -/** @constructor */ function ExitStatus(status) { - this.name = "ExitStatus"; - this.message = `Program terminated with exit(${status})`; - this.status = status; +class ExitStatus { + name="ExitStatus"; + constructor(status) { + this.message = `Program terminated with exit(${status})`; + this.status = status; + } } var callRuntimeCallbacks = callbacks => { - // Pass the module as the first argument. - callbacks.forEach(f => f(Module)); + while (callbacks.length > 0) { + // Pass the module as the first argument. + callbacks.shift()(Module); + } }; var noExitRuntime = Module["noExitRuntime"] || true; -/** @suppress {duplicate } */ function syscallGetVarargI() { +/** @suppress {duplicate } */ var syscallGetVarargI = () => { // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. var ret = HEAP32[((+SYSCALLS.varargs) >> 2)]; SYSCALLS.varargs += 4; return ret; -} +}; var syscallGetVarargP = syscallGetVarargI; @@ -993,7 +1020,7 @@ var mmapAlloc = size => { var MEMFS = { ops_table: null, mount(mount) { - return MEMFS.createNode(null, "/", 16384 | 511, /* 0777 */ 0); + return MEMFS.createNode(null, "/", 16895, 0); }, createNode(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { @@ -1157,7 +1184,7 @@ var MEMFS = { } }, lookup(parent, name) { - throw FS.genericErrors[44]; + throw MEMFS.doesNotExistError; }, mknod(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev); @@ -1202,7 +1229,7 @@ var MEMFS = { return entries; }, symlink(parent, newname, oldpath) { - var node = MEMFS.createNode(parent, newname, 511 | /* 0777 */ 40960, 0); + var node = MEMFS.createNode(parent, newname, 511 | 40960, 0); node.link = oldpath; return node; }, @@ -1422,6 +1449,7 @@ var FS = { initialized: false, ignorePermissions: true, ErrnoError: class { + name="ErrnoError"; // We set the `name` property to be able to identify `FS.ErrnoError` // - the `name` is a standard ECMA-262 property of error objects. Kind of good to have it anyway. // - when using PROXYFS, an error can come from an underlying FS @@ -1429,22 +1457,14 @@ var FS = { // the test `err instanceof FS.ErrnoError` won't detect an error coming from another filesystem, causing bugs. // we'll use the reliable test `err.name == "ErrnoError"` instead constructor(errno) { - // TODO(sbc): Use the inline member declaration syntax once we - // support it in acorn and closure. - this.name = "ErrnoError"; this.errno = errno; } }, - genericErrors: {}, filesystems: null, syncFSRequests: 0, readFiles: {}, FSStream: class { - constructor() { - // TODO(https://github.com/emscripten-core/emscripten/issues/21414): - // Use inline field declarations. - this.shared = {}; - } + shared={}; get object() { return this.node; } @@ -1474,6 +1494,11 @@ var FS = { } }, FSNode: class { + node_ops={}; + stream_ops={}; + readMode=292 | 73; + writeMode=146; + mounted=null; constructor(parent, name, mode, rdev) { if (!parent) { parent = this; @@ -1481,15 +1506,10 @@ var FS = { // root node sets parent to itself this.parent = parent; this.mount = parent.mount; - this.mounted = null; this.id = FS.nextInode++; this.name = name; this.mode = mode; - this.node_ops = {}; - this.stream_ops = {}; this.rdev = rdev; - this.readMode = 292 | 73; - this.writeMode = 146; } get read() { return (this.mode & this.readMode) === this.readMode; @@ -1922,15 +1942,36 @@ var FS = { } return parent.node_ops.mknod(parent, name, mode, dev); }, - create(path, mode) { - mode = mode !== undefined ? mode : 438; - /* 0666 */ mode &= 4095; + statfs(path) { + // NOTE: None of the defaults here are true. We're just returning safe and + // sane values. + var rtn = { + bsize: 4096, + frsize: 4096, + blocks: 1e6, + bfree: 5e5, + bavail: 5e5, + files: FS.nextInode, + ffree: FS.nextInode - 1, + fsid: 42, + flags: 2, + namelen: 255 + }; + var parent = FS.lookupPath(path, { + follow: true + }).node; + if (parent?.node_ops.statfs) { + Object.assign(rtn, parent.node_ops.statfs(parent.mount.opts.root)); + } + return rtn; + }, + create(path, mode = 438) { + mode &= 4095; mode |= 32768; return FS.mknod(path, mode, 0); }, - mkdir(path, mode) { - mode = mode !== undefined ? mode : 511; - /* 0777 */ mode &= 511 | 512; + mkdir(path, mode = 511) { + mode &= 511 | 512; mode |= 16384; return FS.mknod(path, mode, 0); }, @@ -1952,7 +1993,7 @@ var FS = { dev = mode; mode = 438; } - /* 0666 */ mode |= 8192; + mode |= 8192; return FS.mknod(path, mode, dev); }, symlink(oldpath, newpath) { @@ -2048,7 +2089,7 @@ var FS = { // do the underlying fs rename try { old_dir.node_ops.rename(old_node, new_dir, new_name); - // update old node (we do this here to avoid each backend + // update old node (we do this here to avoid each backend // needing to) old_node.parent = new_dir; } catch (e) { @@ -2124,7 +2165,7 @@ var FS = { if (!link.node_ops.readlink) { throw new FS.ErrnoError(28); } - return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link)); + return link.node_ops.readlink(link); }, stat(path, dontFollow) { var lookup = FS.lookupPath(path, { @@ -2239,13 +2280,12 @@ var FS = { timestamp: Math.max(atime, mtime) }); }, - open(path, flags, mode) { + open(path, flags, mode = 438) { if (path === "") { throw new FS.ErrnoError(44); } flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags; if ((flags & 64)) { - mode = typeof mode == "undefined" ? 438 : /* 0666 */ mode; mode = (mode & 4095) | 32768; } else { mode = 0; @@ -2529,7 +2569,8 @@ var FS = { // setup /dev/null FS.registerDevice(FS.makedev(1, 3), { read: () => 0, - write: (stream, buffer, offset, length, pos) => length + write: (stream, buffer, offset, length, pos) => length, + llseek: () => 0 }); FS.mkdev("/dev/null", FS.makedev(1, 3)); // setup /dev/tty and /dev/tty1 @@ -2563,7 +2604,7 @@ var FS = { FS.mkdir("/proc/self/fd"); FS.mount({ mount() { - var node = FS.createNode(proc_self, "fd", 16384 | 511, /* 0777 */ 73); + var node = FS.createNode(proc_self, "fd", 16895, 73); node.node_ops = { lookup(parent, name) { var fd = +name; @@ -2615,11 +2656,6 @@ var FS = { var stderr = FS.open("/dev/stderr", 1); }, staticInit() { - // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info) - [ 44 ].forEach(code => { - FS.genericErrors[code] = new FS.ErrnoError(code); - FS.genericErrors[code].stack = ""; - }); FS.nameTable = new Array(4096); FS.mount(MEMFS, {}, "/"); FS.createDefaultDirectories(); @@ -2810,10 +2846,8 @@ var FS = { // Lazy chunked Uint8Array (implements get and length from Uint8Array). // Actual getting is abstracted away for eventual reuse. class LazyUint8Array { - constructor() { - this.lengthKnown = false; - this.chunks = []; - } + lengthKnown=false; + chunks=[]; // Loaded chunks. Index is the chunk number get(idx) { if (idx > this.length - 1 || idx < 0) { @@ -3280,9 +3314,7 @@ function ___syscall_unlinkat(dirfd, path, flags) { } } -var __abort_js = () => { - abort(""); -}; +var __abort_js = () => abort(""); var __emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num); @@ -3500,6 +3532,12 @@ FS.createPreloadedFile = FS_createPreloadedFile; FS.staticInit(); +// This error may happen quite a bit. To avoid overhead we reuse it (and +// suffer a lack of stack info). +MEMFS.doesNotExistError = new FS.ErrnoError(44); + +/** @suppress {checkTypes} */ MEMFS.doesNotExistError.stack = ""; + var wasmImports = { /** @export */ __syscall_fcntl64: ___syscall_fcntl64, /** @export */ __syscall_ioctl: ___syscall_ioctl, @@ -3538,8 +3576,6 @@ var dynCall_jiji = Module["dynCall_jiji"] = (a0, a1, a2, a3, a4) => (dynCall_jij // === Auto-generated postamble setup entry stuff === var calledRun; -var calledPrerun; - dependenciesFulfilled = function runCaller() { // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) if (!calledRun) run(); @@ -3572,23 +3608,21 @@ function run(args = arguments_) { if (runDependencies > 0) { return; } - if (!calledPrerun) { - calledPrerun = 1; - preRun(); - // a preRun added a dependency, run will be called later - if (runDependencies > 0) { - return; - } + preRun(); + // a preRun added a dependency, run will be called later + if (runDependencies > 0) { + return; } function doRun() { // run may have just been called through dependencies being fulfilled just in this very frame, // or while the async setStatus time below was happening if (calledRun) return; - calledRun = 1; - Module["calledRun"] = 1; + calledRun = true; + Module["calledRun"] = true; if (ABORT) return; initRuntime(); preMain(); + readyPromiseResolve(Module); Module["onRuntimeInitialized"]?.(); if (shouldRunNow) callMain(args); postRun(); @@ -3617,20 +3651,21 @@ var shouldRunNow = true; if (Module["noInitialRun"]) shouldRunNow = false; run(); -} -class Benchmark { - async runIteration() { - if (!Module["_main"]) { - let runtimeInitializedCallback; - let runtimeInitialized = new Promise((success) => runtimeInitializedCallback = success); - Module.onRuntimeInitialized = function() { - runtimeInitializedCallback(); - } - setup(Module); - await runtimeInitialized; - } +// end include: postamble.js +// include: postamble_modularize.js +// In MODULARIZE mode we wrap the generated code in a factory function +// and return either the Module itself, or a promise of the module. +// We assign to the `moduleRtn` global here and configure closure to see +// this as and extern so it won't get minified. +moduleRtn = readyPromise; - Module["_runIteration"](); - } + + return moduleRtn; } +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = setupModule; +else if (typeof define === 'function' && define['amd']) + define([], () => setupModule); diff --git a/wasm/TSF/tsf.js.symbols b/wasm/TSF/tsf.js.symbols index cb9fd46..6cca3e5 100644 --- a/wasm/TSF/tsf.js.symbols +++ b/wasm/TSF/tsf.js.symbols @@ -248,12 +248,12 @@ 247:sn_write 248:__wasi_syscall_ret 249:wctomb -250:sbrk -251:emscripten_builtin_malloc -252:emscripten_builtin_free -253:dlrealloc -254:dispose_chunk -255:emscripten_builtin_calloc +250:emscripten_builtin_malloc +251:emscripten_builtin_free +252:dlrealloc +253:dispose_chunk +254:emscripten_builtin_calloc +255:sbrk 256:_emscripten_stack_restore 257:_emscripten_stack_alloc 258:emscripten_stack_get_current diff --git a/wasm/TSF/tsf.wasm b/wasm/TSF/tsf.wasm index d2022f65710c2d4bb412a8ba6012b1edf2f31e7f..f027dcfccdfb7f223615f318f759055100612ec7 100755 GIT binary patch delta 10760 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 Date: Wed, 18 Dec 2024 10:07:43 -0500 Subject: [PATCH 3/4] Move Benchmark class to benchmark.js rather than have it as part of the generated code. Remove some stale logging. Remove main from TSF-wasm since it's no longer used. Make the count value an argument rather than a constant in C code. This appears to make the performance characteristics similar to what it was before this refactor. Old sampling (after recompile so wasm function numbers are meaningful): Sampling rate: 1000.000000 microseconds. Total samples: 364 Top functions as 199 '.wasm-function[100]#:4294967295' 34 '.wasm-function[238]#:4294967295' 19 '.wasm-function[208]#:4294967295' 16 '_platform_memmove#:4294967295' 12 '.wasm-function[251]#:4294967295' 11 '.wasm-function[232]#:4294967295' 9 '.wasm-function[209]#:4294967295' 8 '.wasm-function[237]#:4294967295' 7 '.wasm-function[201]#:4294967295' 5 '.wasm-function[250]#:4294967295' 4 '.wasm-function[213]#:4294967295' 3 '.wasm-function[216]#:4294967295' Sampling rate: 1000.000000 microseconds. Total samples: 364 Tier breakdown: ----------------------------------- LLInt: 0 (0.000000%) Baseline: 1 (0.274725%) DFG: 0 (0.000000%) FTL: 0 (0.000000%) js builtin: 0 (0.000000%) IPInt: 0 (0.000000%) WasmLLInt: 3 (0.824176%) BBQ: 211 (57.967033%) OMG: 116 (31.868132%) Wasm: 0 (0.000000%) Host: 0 (0.000000%) RegExp: 0 (0.000000%) C/C++: 30 (8.241758%) Unknown Frame: 3 (0.824176%) Hottest bytecodes as 33 '.wasm-function[100]:BBQMode:0x2ed' 33 '.wasm-function[100]:BBQMode:0x2f0' 16 '_platform_memmove#:None:' 8 '.wasm-function[100]:BBQMode:0xa733' 7 '.wasm-function[100]:BBQMode:0x70a2' 6 '.wasm-function[100]:BBQMode:0x6f8a' 6 '.wasm-function[100]:BBQMode:0x6f94' 5 '.wasm-function[208]:OMGMode:nil' 5 '.wasm-function[100]:BBQMode:0x5b' 5 '.wasm-function[100]:BBQMode:0x6f9b' 4 '.wasm-function[100]:BBQMode:0x5df3' ... New sampling with this PR: Sampling rate: 1000.000000 microseconds. Total samples: 4908 Top functions as 2763 '.wasm-function[100]#:4294967295' 464 '.wasm-function[238]#:4294967295' 436 '.wasm-function[208]#:4294967295' 138 '.wasm-function[251]#:4294967295' 128 '.wasm-function[237]#:4294967295' 117 '.wasm-function[232]#:4294967295' 108 '.wasm-function[250]#:4294967295' 107 '.wasm-function[209]#:4294967295' 104 '.wasm-function[201]#:4294967295' 77 '.wasm-function[216]#:4294967295' 54 '.wasm-function[226]#:4294967295' 43 '.wasm-function[162]#:4294967295' Sampling rate: 1000.000000 microseconds. Total samples: 4908 Tier breakdown: ----------------------------------- LLInt: 0 (0.000000%) Baseline: 2 (0.040750%) DFG: 2 (0.040750%) FTL: 5 (0.101874%) js builtin: 1 (0.020375%) IPInt: 0 (0.000000%) WasmLLInt: 3 (0.061125%) BBQ: 2771 (56.458843%) OMG: 1988 (40.505297%) Wasm: 0 (0.000000%) Host: 0 (0.000000%) RegExp: 0 (0.000000%) C/C++: 95 (1.935615%) Unknown Frame: 41 (0.835371%) Unknown Executable: 1 (0.020375%) Hottest bytecodes as 526 '.wasm-function[100]:BBQMode:0x2ed' 451 '.wasm-function[100]:BBQMode:0x2f0' 125 '.wasm-function[208]:OMGMode:0x0' 105 '.wasm-function[100]:BBQMode:0x6f8a' 91 '.wasm-function[100]:BBQMode:0xa724' 84 '.wasm-function[100]:BBQMode:0x5b' 81 '.wasm-function[100]:BBQMode:0xa733' 79 '.wasm-function[100]:BBQMode:0x6f94' 77 '.wasm-function[100]:BBQMode:nil' 63 '.wasm-function[208]:OMGMode:0x16d' ... It looks like versions have similar sample rates for the top function. There's more samples in the top tier compiler but that's presumably ok and expected as the test runs longer. --- JetStreamDriver.js | 23 ++------ wasm/TSF/benchmark.js | 33 +++++++++++ wasm/TSF/build.sh | 2 +- wasm/TSF/tsf.js | 64 +-------------------- wasm/TSF/tsf.js.symbols | 119 ++++++++++++++++++++-------------------- wasm/TSF/tsf.wasm | Bin 144423 -> 144344 bytes wasm/TSF/tsf_ir_speed.c | 48 +--------------- 7 files changed, 102 insertions(+), 187 deletions(-) create mode 100644 wasm/TSF/benchmark.js diff --git a/JetStreamDriver.js b/JetStreamDriver.js index f72c797..7acf9e5 100644 --- a/JetStreamDriver.js +++ b/JetStreamDriver.js @@ -164,7 +164,7 @@ function uiFriendlyDuration(time) const minutes = time.getMinutes(); const seconds = time.getSeconds(); const milliSeconds = time.getMilliseconds(); - const result = "" + minutes + ":"; + let result = "" + minutes + ":"; result = result + (seconds < 10 ? "0" : "") + seconds + "."; result = result + (milliSeconds < 10 ? "00" : (milliSeconds < 100 ? "0" : "")) + milliSeconds; @@ -997,7 +997,6 @@ class AsyncBenchmark extends DefaultBenchmark { await __benchmark.runIteration(); let end = performance.now(); ${this.postIterationCode} - console.log("iteration i: " + (end - start)); results.push(Math.max(1, end - start)); } if (__benchmark.validate) @@ -1008,9 +1007,8 @@ class AsyncBenchmark extends DefaultBenchmark { } }; -// Meant for wasm benchmarks that are directly compiled with an emcc script and not part of a larger project's build system. -// Requires the following flags to emcc `-s MODULARIZE=1 -s EXPORT_NAME=setupModule -s EXPORTED_FUNCTIONS=_runIteration` -// in addition to any other benchmark specific flags. +// Meant for wasm benchmarks that are directly compiled with an emcc build script. It might not work for benchmarks built as +// part of a larger project's build system or a wasm benchmark compiled from a language that doesn't compile with emcc. class WasmEMCCBenchmark extends AsyncBenchmark { get prerunCode() { let str = ` @@ -1084,18 +1082,8 @@ class WasmEMCCBenchmark extends AsyncBenchmark { let keys = Object.keys(this.plan.preload); for (let i = 0; i < keys.length; ++i) { - str += `loadBlob("${keys[i]}", "${this.plan.preload[keys[i]]}", () => {\n`; + str += `loadBlob("${keys[i]}", "${this.plan.preload[keys[i]]}", async () => {\n`; } - str += ` - class Benchmark { - async runIteration() { - if (!Module["_runIteration"]) - await setupModule(Module); - - Module["_runIteration"](); - } - }; - ` str += super.runnerCode; for (let i = 0; i < keys.length; ++i) { @@ -1903,7 +1891,8 @@ const testPlans = [ { name: "tsf-wasm", files: [ - "./wasm/TSF/tsf.js" + "./wasm/TSF/tsf.js", + "./wasm/TSF/benchmark.js", ], preload: { wasmBinary: "./wasm/TSF/tsf.wasm" diff --git a/wasm/TSF/benchmark.js b/wasm/TSF/benchmark.js new file mode 100644 index 0000000..405e126 --- /dev/null +++ b/wasm/TSF/benchmark.js @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 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. +*/ + +class Benchmark { + async runIteration() { + if (!Module._runIteration) + await setupModule(Module); + + Module._runIteration(150); + } +}; diff --git a/wasm/TSF/build.sh b/wasm/TSF/build.sh index 93c526d..050cb94 100755 --- a/wasm/TSF/build.sh +++ b/wasm/TSF/build.sh @@ -3,7 +3,7 @@ set -euo pipefail emcc \ - -o tsf.js -O2 -s MODULARIZE=1 -s EXPORT_NAME=setupModule -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 --emit-symbol-map -s EXPORTED_FUNCTIONS=_main,_runIteration \ + -o tsf.js -O2 -s MODULARIZE=1 -s EXPORT_NAME=setupModule -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 --emit-symbol-map -s EXPORTED_FUNCTIONS=_runIteration \ -I. -DTSF_BUILD_SYSTEM=1 \ tsf_asprintf.c\ tsf_buffer.c\ diff --git a/wasm/TSF/tsf.js b/wasm/TSF/tsf.js index fe388b8..dab0e9b 100644 --- a/wasm/TSF/tsf.js +++ b/wasm/TSF/tsf.js @@ -250,9 +250,6 @@ var __ATPRERUN__ = []; // functions called before the runtime is initialized var __ATINIT__ = []; -// functions called during startup -var __ATMAIN__ = []; - // functions called during shutdown var __ATPOSTRUN__ = []; @@ -277,10 +274,6 @@ function initRuntime() { callRuntimeCallbacks(__ATINIT__); } -function preMain() { - callRuntimeCallbacks(__ATMAIN__); -} - function postRun() { if (Module["postRun"]) { if (typeof Module["postRun"] == "function") Module["postRun"] = [ Module["postRun"] ]; @@ -3505,29 +3498,6 @@ function _fd_write(fd, iov, iovcnt, pnum) { } } -var handleException = e => { - // Certain exception types we do not treat as errors since they are used for - // internal control flow. - // 1. ExitStatus, which is thrown by exit() - // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others - // that wish to return to JS event loop. - if (e instanceof ExitStatus || e == "unwind") { - return EXITSTATUS; - } - quit_(1, e); -}; - -var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); - -var stackAlloc = sz => __emscripten_stack_alloc(sz); - -var stringToUTF8OnStack = str => { - var size = lengthBytesUTF8(str) + 1; - var ret = stackAlloc(size); - stringToUTF8(str, ret, size); - return ret; -}; - FS.createPreloadedFile = FS_createPreloadedFile; FS.staticInit(); @@ -3560,9 +3530,7 @@ var wasmExports = createWasm(); var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports["__wasm_call_ctors"])(); -var _main = Module["_main"] = (a0, a1) => (_main = Module["_main"] = wasmExports["__main_argc_argv"])(a0, a1); - -var _runIteration = Module["_runIteration"] = () => (_runIteration = Module["_runIteration"] = wasmExports["runIteration"])(); +var _runIteration = Module["_runIteration"] = a0 => (_runIteration = Module["_runIteration"] = wasmExports["runIteration"])(a0); var __emscripten_stack_restore = a0 => (__emscripten_stack_restore = wasmExports["_emscripten_stack_restore"])(a0); @@ -3583,28 +3551,7 @@ dependenciesFulfilled = function runCaller() { }; // try this again later, after new deps are fulfilled -function callMain(args = []) { - var entryFunction = _main; - args.unshift(thisProgram); - var argc = args.length; - var argv = stackAlloc((argc + 1) * 4); - var argv_ptr = argv; - args.forEach(arg => { - HEAPU32[((argv_ptr) >> 2)] = stringToUTF8OnStack(arg); - argv_ptr += 4; - }); - HEAPU32[((argv_ptr) >> 2)] = 0; - try { - var ret = entryFunction(argc, argv); - // if we're not running an evented main loop, it's time to exit - exitJS(ret, /* implicit = */ true); - return ret; - } catch (e) { - return handleException(e); - } -} - -function run(args = arguments_) { +function run() { if (runDependencies > 0) { return; } @@ -3621,10 +3568,8 @@ function run(args = arguments_) { Module["calledRun"] = true; if (ABORT) return; initRuntime(); - preMain(); readyPromiseResolve(Module); Module["onRuntimeInitialized"]?.(); - if (shouldRunNow) callMain(args); postRun(); } if (Module["setStatus"]) { @@ -3645,11 +3590,6 @@ if (Module["preInit"]) { } } -// shouldRunNow refers to calling main(), not run(). -var shouldRunNow = true; - -if (Module["noInitialRun"]) shouldRunNow = false; - run(); // end include: postamble.js diff --git a/wasm/TSF/tsf.js.symbols b/wasm/TSF/tsf.js.symbols index 6cca3e5..e161e3c 100644 --- a/wasm/TSF/tsf.js.symbols +++ b/wasm/TSF/tsf.js.symbols @@ -198,63 +198,62 @@ 197:DProcedureDefn__variables__get_type 198:DProcedureDefn__code__get_type 199:DProgram__globals__get_type -200:main -201:runIteration -202:__stdio_close -203:__stdio_read -204:__stdio_seek -205:__stdio_write -206:abort -207:close -208:__memcpy -209:__memset -210:__gettimeofday -211:fflush -212:fiprintf -213:__towrite -214:__overflow -215:fputc -216:__fwritex -217:getenv -218:__bswap_32 -219:__lseek -220:open -221:__small_printf -222:read -223:snprintf -224:__emscripten_stdout_close -225:__emscripten_stdout_seek -226:__stpcpy -227:strchr -228:__strchrnul -229:strcmp -230:strdup -231:__strerror_l -232:strlen -233:__syscall_ret -234:tolower -235:vasprintf -236:frexp -237:__vfprintf_internal -238:printf_core -239:out -240:getint -241:pop_arg -242:fmt_u -243:pad -244:fmt_fp -245:pop_arg_long_double -246:vsnprintf -247:sn_write -248:__wasi_syscall_ret -249:wctomb -250:emscripten_builtin_malloc -251:emscripten_builtin_free -252:dlrealloc -253:dispose_chunk -254:emscripten_builtin_calloc -255:sbrk -256:_emscripten_stack_restore -257:_emscripten_stack_alloc -258:emscripten_stack_get_current -259:legalstub$dynCall_jiji +200:runIteration +201:__stdio_close +202:__stdio_read +203:__stdio_seek +204:__stdio_write +205:abort +206:close +207:__memcpy +208:__memset +209:__gettimeofday +210:fflush +211:fiprintf +212:__towrite +213:__overflow +214:fputc +215:__fwritex +216:getenv +217:__bswap_32 +218:__lseek +219:open +220:__small_printf +221:read +222:snprintf +223:__emscripten_stdout_close +224:__emscripten_stdout_seek +225:__stpcpy +226:strchr +227:__strchrnul +228:strcmp +229:strdup +230:__strerror_l +231:strlen +232:__syscall_ret +233:tolower +234:vasprintf +235:frexp +236:__vfprintf_internal +237:printf_core +238:out +239:getint +240:pop_arg +241:fmt_u +242:pad +243:fmt_fp +244:pop_arg_long_double +245:vsnprintf +246:sn_write +247:__wasi_syscall_ret +248:wctomb +249:emscripten_builtin_malloc +250:emscripten_builtin_free +251:dlrealloc +252:dispose_chunk +253:emscripten_builtin_calloc +254:sbrk +255:_emscripten_stack_restore +256:_emscripten_stack_alloc +257:emscripten_stack_get_current +258:legalstub$dynCall_jiji diff --git a/wasm/TSF/tsf.wasm b/wasm/TSF/tsf.wasm index f027dcfccdfb7f223615f318f759055100612ec7..a159644104c40304de9d7aef17a1e00738a2daaf 100755 GIT binary patch delta 11448 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 Date: Fri, 20 Dec 2024 07:43:20 -0700 Subject: [PATCH 4/4] Address Danleh's comments: Build products are now built to a build subdirectory. Log the build info into build.log Fix `date` command to work on MacOS out of the box. Reduce iteration count to 15/2. Remove ZLib check since it's always false in JetStream anyway. --- JetStreamDriver.js | 22 +++++++++++++++------- sqlite3/build.sh | 3 ++- wasm/TSF/build.log | 6 ++++++ wasm/TSF/build.sh | 12 +++++++++++- wasm/TSF/{ => build}/tsf.js | 0 wasm/TSF/{ => build}/tsf.js.symbols | 0 wasm/TSF/{ => build}/tsf.wasm | Bin 144344 -> 144147 bytes wasm/TSF/tsf_ir_speed.c | 8 +------- 8 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 wasm/TSF/build.log rename wasm/TSF/{ => build}/tsf.js (100%) rename wasm/TSF/{ => build}/tsf.js.symbols (100%) rename wasm/TSF/{ => build}/tsf.wasm (63%) diff --git a/JetStreamDriver.js b/JetStreamDriver.js index 7acf9e5..0f5c613 100644 --- a/JetStreamDriver.js +++ b/JetStreamDriver.js @@ -336,7 +336,13 @@ class Driver { } else globalObject = runString(""); - globalObject.console = { log: globalObject.print, warn: (e) => { print("Warn: " + e); /*$vm.abort();*/ }, error: (e) => { print("Error: " + e); /*$vm.abort();*/ } } + globalObject.console = { + log: globalObject.print, + warn: (e) => { print("Warn: " + e); }, + error: (e) => { print("Error: " + e); }, + debug: (e) => { print("Debug: " + e); }, + }; + globalObject.self = globalObject; globalObject.top = { currentResolve, @@ -1012,7 +1018,7 @@ class AsyncBenchmark extends DefaultBenchmark { class WasmEMCCBenchmark extends AsyncBenchmark { get prerunCode() { let str = ` - let verbose = true; + let verbose = false; let globalObject = this; @@ -1030,8 +1036,8 @@ class WasmEMCCBenchmark extends AsyncBenchmark { let Module = { preRun: [], postRun: [], - print: function() { }, - printErr: function() { }, + print: print, + printErr: printErr, setStatus: function(text) { }, totalDependencies: 0, @@ -1891,13 +1897,15 @@ const testPlans = [ { name: "tsf-wasm", files: [ - "./wasm/TSF/tsf.js", + "./wasm/TSF/build/tsf.js", "./wasm/TSF/benchmark.js", ], preload: { - wasmBinary: "./wasm/TSF/tsf.wasm" + wasmBinary: "./wasm/TSF/build/tsf.wasm" }, benchmarkClass: WasmEMCCBenchmark, + iterations: 15, + worstCaseCount: 2, testGroup: WasmGroup }, { @@ -1943,7 +1951,7 @@ const testPlans = [ preload: { wasmBinary: "./sqlite3/build/jswasm/speedtest1.wasm" }, - benchmarkClass: WasmBenchmark, + benchmarkClass: WasmLegacyBenchmark, testGroup: WasmGroup }, { diff --git a/sqlite3/build.sh b/sqlite3/build.sh index ab9568d..ff7b067 100755 --- a/sqlite3/build.sh +++ b/sqlite3/build.sh @@ -8,8 +8,9 @@ rm sqlite-src-*.zip rm -rf sqlite-src-*/ rm -rf build/ +touch build.log BUILD_LOG="$(realpath build.log)" -echo -e "Built on $(date --rfc-3339=seconds)\n" | tee "$BUILD_LOG" +echo -e "Built on $(date -u '+%Y-%m-%dT%H:%M:%SZ')\n" | tee "$BUILD_LOG" echo "Toolchain versions" | tee -a "$BUILD_LOG" emcc --version | head -n1 | tee -a "$BUILD_LOG" diff --git a/wasm/TSF/build.log b/wasm/TSF/build.log new file mode 100644 index 0000000..febb95e --- /dev/null +++ b/wasm/TSF/build.log @@ -0,0 +1,6 @@ +Built on 2024-12-20T14:36:37Z + +Toolchain versions +emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.73-git +Building... +Building done diff --git a/wasm/TSF/build.sh b/wasm/TSF/build.sh index 050cb94..ac73360 100755 --- a/wasm/TSF/build.sh +++ b/wasm/TSF/build.sh @@ -2,8 +2,17 @@ set -euo pipefail +touch build.log +BUILD_LOG="$(realpath build.log)" +echo "Built on $(date -u '+%Y-%m-%dT%H:%M:%SZ')\n" | tee "$BUILD_LOG" + +echo "Toolchain versions" | tee -a "$BUILD_LOG" +emcc --version | head -n1 | tee -a "$BUILD_LOG" + +echo "Building..." | tee -a "$BUILD_LOG" +mkdir -p build emcc \ - -o tsf.js -O2 -s MODULARIZE=1 -s EXPORT_NAME=setupModule -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 --emit-symbol-map -s EXPORTED_FUNCTIONS=_runIteration \ + -o build/tsf.js -O2 -s MODULARIZE=1 -s EXPORT_NAME=setupModule -s WASM=1 -s TOTAL_MEMORY=52428800 -g1 --emit-symbol-map -s EXPORTED_FUNCTIONS=_runIteration \ -I. -DTSF_BUILD_SYSTEM=1 \ tsf_asprintf.c\ tsf_buffer.c\ @@ -56,3 +65,4 @@ emcc \ tsf_ir_different.c\ tsf_ir_speed.c +echo "Building done" | tee -a "$BUILD_LOG" diff --git a/wasm/TSF/tsf.js b/wasm/TSF/build/tsf.js similarity index 100% rename from wasm/TSF/tsf.js rename to wasm/TSF/build/tsf.js diff --git a/wasm/TSF/tsf.js.symbols b/wasm/TSF/build/tsf.js.symbols similarity index 100% rename from wasm/TSF/tsf.js.symbols rename to wasm/TSF/build/tsf.js.symbols diff --git a/wasm/TSF/tsf.wasm b/wasm/TSF/build/tsf.wasm similarity index 63% rename from wasm/TSF/tsf.wasm rename to wasm/TSF/build/tsf.wasm index a159644104c40304de9d7aef17a1e00738a2daaf..c56dbcc497d0e9a37c82766e5cdc592b7fe0b36c 100755 GIT binary patch delta 18242 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 delta 18415 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 Cd*Ceq diff --git a/wasm/TSF/tsf_ir_speed.c b/wasm/TSF/tsf_ir_speed.c index 3e4120b..05eb656 100644 --- a/wasm/TSF/tsf_ir_speed.c +++ b/wasm/TSF/tsf_ir_speed.c @@ -330,13 +330,7 @@ int runIteration(unsigned count) { TIMEIT(readMallocTest(filename, count)); TIMEIT(readConvertTest(filename, count)); - // TODO: tsf_zlib_supported is false so we should just remove this? - if (tsf_zlib_supported()) { - TIMEIT(writeTest(zipFilename, 100, count, TSF_ZIP_ZLIB)); - TIMEIT(readTest(zipFilename, count)); - TIMEIT(readMallocTest(filename, count)); - TIMEIT(readConvertTest(zipFilename, count)); - } + /* We don't benchmark zlib because it's not supported in JetStream */ /* We don't benchmark bzip2 because it's just too slow to be interesting. */