scran.js
Version:
Single cell RNA-seq analysis in Javascript
1,314 lines (1,213 loc) • 247 kB
JavaScript
// This code implements the `-sMODULARIZE` settings by taking the generated
// JS program code (INNER_JS_CODE) and wrapping it in a factory function.
// When targetting node and ES6 we use `await import ..` in the generated code
// so the outer function needs to be marked as async.
async function loadScran(moduleArg = {}) {
var moduleRtn;
// include: shell.js
// include: minimum_runtime_check.js
// end include: minimum_runtime_check.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<Module>
// 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 = moduleArg;
// 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 = !!globalThis.window;
var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope;
// N.b. Electron.js environment is simultaneously a NODE-environment, but
// also a web environment.
var ENVIRONMENT_IS_NODE = globalThis.process?.versions?.node && globalThis.process?.type != "renderer";
// Three configurations we can be running in:
// 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false)
// 2) We could be the application main() thread proxied to worker. (with Emscripten -sPROXY_TO_WORKER) (ENVIRONMENT_IS_WORKER == true, ENVIRONMENT_IS_PTHREAD == false)
// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true)
// The way we signal to a worker that it is hosting a pthread is to construct
// it with a specific name.
var ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && self.name?.startsWith("em-pthread");
if (ENVIRONMENT_IS_NODE) {
// When building an ES module `require` is not normally available.
// We need to use `createRequire()` to construct the require()` function.
const {createRequire} = await import("module");
/** @suppress{duplicate} */ var require = createRequire(import.meta.url);
var worker_threads = require("worker_threads");
global.Worker = worker_threads.Worker;
ENVIRONMENT_IS_WORKER = !worker_threads.isMainThread;
// Under node we set `workerData` to `em-pthread` to signal that the worker
// is hosting a pthread.
ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && worker_threads["workerData"] == "em-pthread";
}
// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
var arguments_ = [];
var thisProgram = "./this.program";
var quit_ = (status, toThrow) => {
throw toThrow;
};
var _scriptName = import.meta.url;
// `/` 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");
if (_scriptName.startsWith("file:")) {
scriptDirectory = require("path").dirname(require("url").fileURLToPath(_scriptName)) + "/";
}
// include: node_shell_read.js
readBinary = filename => {
// We need to re-wrap `file://` strings to URLs.
filename = isFileURI(filename) ? new URL(filename) : filename;
var ret = fs.readFileSync(filename);
return ret;
};
readAsync = async (filename, binary = true) => {
// See the comment in the `readBinary` function.
filename = isFileURI(filename) ? new URL(filename) : filename;
var ret = fs.readFileSync(filename, binary ? undefined : "utf8");
return ret;
};
// end include: node_shell_read.js
if (process.argv.length > 1) {
thisProgram = process.argv[1].replace(/\\/g, "/");
}
arguments_ = process.argv.slice(2);
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) {
try {
scriptDirectory = new URL(".", _scriptName).href;
} catch {}
// Differentiate the Web Worker from the Node Worker case, as reading must
// be done differently.
if (!ENVIRONMENT_IS_NODE) {
// 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 = async url => {
var response = await fetch(url, {
credentials: "same-origin"
});
if (response.ok) {
return response.arrayBuffer();
}
throw new Error(response.status + " : " + response.url);
};
}
} else {}
// Set up the out() and err() hooks, which are how we can print to stdout or
// stderr, respectively.
// Normally just binding console.log/console.error here works fine, but
// under node (with workers) we see missing/out-of-order messages so route
// directly to stdout and stderr.
// See https://github.com/emscripten-core/emscripten/issues/14804
var defaultPrint = console.log.bind(console);
var defaultPrintErr = console.error.bind(console);
if (ENVIRONMENT_IS_NODE) {
var utils = require("util");
var stringify = a => typeof a == "object" ? utils.inspect(a) : a;
defaultPrint = (...args) => fs.writeSync(1, args.map(stringify).join(" ") + "\n");
defaultPrintErr = (...args) => fs.writeSync(2, args.map(stringify).join(" ") + "\n");
}
var out = defaultPrint;
var err = defaultPrintErr;
// 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;
// Wasm globals
// For sending to workers.
var wasmModule;
//========================================
// 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;
/**
* Indicates whether filename is delivered via file protocol (as opposed to http/https)
* @noinline
*/ var isFileURI = filename => filename.startsWith("file://");
// include: runtime_common.js
// include: runtime_stack_check.js
// end include: runtime_stack_check.js
// include: runtime_exceptions.js
// end include: runtime_exceptions.js
// include: runtime_debug.js
// end include: runtime_debug.js
// Support for growable heap + pthreads, where the buffer may change, so JS views
// must be updated.
function growMemViews() {
// `updateMemoryViews` updates all the views simultaneously, so it's enough to check any of them.
if (wasmMemory.buffer != HEAP8.buffer) {
updateMemoryViews();
}
}
var readyPromiseResolve, readyPromiseReject;
if (ENVIRONMENT_IS_NODE && (ENVIRONMENT_IS_PTHREAD)) {
// Create as web-worker-like an environment as we can.
var parentPort = worker_threads["parentPort"];
parentPort.on("message", msg => global.onmessage?.({
data: msg
}));
Object.assign(globalThis, {
self: global,
postMessage: msg => parentPort["postMessage"](msg)
});
// Node.js Workers do not pass postMessage()s and uncaught exception events to the parent
// thread necessarily in the same order where they were generated in sequential program order.
// See https://github.com/nodejs/node/issues/59617
// To remedy this, capture all uncaughtExceptions in the Worker, and sequentialize those over
// to the same postMessage pipe that other messages use.
process.on("uncaughtException", err => {
postMessage({
cmd: "uncaughtException",
error: err
});
// Also shut down the Worker to match the same semantics as if this uncaughtException
// handler was not registered.
// (n.b. this will not shut down the whole Node.js app process, but just the Worker)
process.exit(1);
});
}
// include: runtime_pthread.js
// Pthread Web Worker handling code.
// This code runs only on pthread web workers and handles pthread setup
// and communication with the main thread via postMessage.
var startWorker;
if (ENVIRONMENT_IS_PTHREAD) {
// Thread-local guard variable for one-time init of the JS state
var initializedJS = false;
// Turn unhandled rejected promises into errors so that the main thread will be
// notified about them.
self.onunhandledrejection = e => {
throw e.reason || e;
};
function handleMessage(e) {
try {
var msgData = e["data"];
//dbg('msgData: ' + Object.keys(msgData));
var cmd = msgData.cmd;
if (cmd === "load") {
// Preload command that is called once per worker to parse and load the Emscripten code.
// Until we initialize the runtime, queue up any further incoming messages.
let messageQueue = [];
self.onmessage = e => messageQueue.push(e);
// And add a callback for when the runtime is initialized.
startWorker = () => {
// Notify the main thread that this thread has loaded.
postMessage({
cmd: "loaded"
});
// Process any messages that were queued before the thread was ready.
for (let msg of messageQueue) {
handleMessage(msg);
}
// Restore the real message handler.
self.onmessage = handleMessage;
};
// Use `const` here to ensure that the variable is scoped only to
// that iteration, allowing safe reference from a closure.
for (const handler of msgData.handlers) {
// The the main module has a handler for a certain even, but no
// handler exists on the pthread worker, then proxy that handler
// back to the main thread.
if (!Module[handler] || Module[handler].proxy) {
Module[handler] = (...args) => {
postMessage({
cmd: "callHandler",
handler,
args
});
};
// Rebind the out / err handlers if needed
if (handler == "print") out = Module[handler];
if (handler == "printErr") err = Module[handler];
}
}
wasmMemory = msgData.wasmMemory;
updateMemoryViews();
wasmModule = msgData.wasmModule;
createWasm();
run();
} else if (cmd === "run") {
// Call inside JS module to set up the stack frame for this pthread in JS module scope.
// This needs to be the first thing that we do, as we cannot call to any C/C++ functions
// until the thread stack is initialized.
establishStackSpace(msgData.pthread_ptr);
// Pass the thread address to wasm to store it for fast access.
__emscripten_thread_init(msgData.pthread_ptr, /*is_main=*/ 0, /*is_runtime=*/ 0, /*can_block=*/ 1, 0, 0);
PThread.threadInitTLS();
// Await mailbox notifications with `Atomics.waitAsync` so we can start
// using the fast `Atomics.notify` notification path.
__emscripten_thread_mailbox_await(msgData.pthread_ptr);
if (!initializedJS) {
// Embind must initialize itself on all threads, as it generates support JS.
// We only do this once per worker since they get reused
__embind_initialize_bindings();
initializedJS = true;
}
try {
invokeEntryPoint(msgData.start_routine, msgData.arg);
} catch (ex) {
if (ex != "unwind") {
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
// would make this thread joinable). Instead, re-throw the exception
// and let the top level handler propagate it back to the main thread.
throw ex;
}
}
} else if (msgData.target === "setimmediate") {} else if (cmd === "checkMailbox") {
if (initializedJS) {
checkMailbox();
}
} else if (cmd) {
// The received message looks like something that should be handled by this message
// handler, (since there is a cmd field present), but is not one of the
// recognized commands:
err(`worker: received unknown command ${cmd}`);
err(msgData);
}
} catch (ex) {
__emscripten_thread_crashed();
throw ex;
}
}
self.onmessage = handleMessage;
}
// ENVIRONMENT_IS_PTHREAD
// end include: runtime_pthread.js
// 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;
// BigInt64Array type is not correctly defined in closure
var /** not-@type {!BigInt64Array} */ HEAP64, /* BigUint64Array type is not correctly defined in closure
/** not-@type {!BigUint64Array} */ HEAPU64;
var runtimeInitialized = false;
function updateMemoryViews() {
var b = wasmMemory.buffer;
Module["HEAP8"] = HEAP8 = new Int8Array(b);
HEAP16 = new Int16Array(b);
HEAPU8 = new Uint8Array(b);
HEAPU16 = new Uint16Array(b);
HEAP32 = new Int32Array(b);
HEAPU32 = new Uint32Array(b);
HEAPF32 = new Float32Array(b);
HEAPF64 = new Float64Array(b);
HEAP64 = new BigInt64Array(b);
HEAPU64 = new BigUint64Array(b);
}
// In non-standalone/normal mode, we create the memory here.
// include: runtime_init_memory.js
// Create the wasm memory. (Note: this only applies if IMPORTED_MEMORY is defined)
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
function initMemory() {
if ((ENVIRONMENT_IS_PTHREAD)) {
return;
}
if (Module["wasmMemory"]) {
wasmMemory = Module["wasmMemory"];
} else {
var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216;
/** @suppress {checkTypes} */ wasmMemory = new WebAssembly.Memory({
"initial": BigInt(INITIAL_MEMORY / 65536),
// In theory we should not need to emit the maximum if we want "unlimited"
// or 4GB of memory, but VMs error on that atm, see
// https://github.com/emscripten-core/emscripten/issues/14130
// And in the pthreads case we definitely need to emit a maximum. So
// always emit one.
"maximum": 262144n,
"shared": true,
"address": "i64"
});
}
updateMemoryViews();
}
// end include: runtime_init_memory.js
// include: memoryprofiler.js
// end include: memoryprofiler.js
// end include: runtime_common.js
function preRun() {
if (Module["preRun"]) {
if (typeof Module["preRun"] == "function") Module["preRun"] = [ Module["preRun"] ];
while (Module["preRun"].length) {
addOnPreRun(Module["preRun"].shift());
}
}
// Begin ATPRERUNS hooks
callRuntimeCallbacks(onPreRuns);
}
function initRuntime() {
runtimeInitialized = true;
if (ENVIRONMENT_IS_PTHREAD) return startWorker();
// Begin ATINITS hooks
if (!Module["noFSInit"] && !FS.initialized) FS.init();
TTY.init();
// End ATINITS hooks
wasmExports["sa"]();
// Begin ATPOSTCTORS hooks
FS.ignorePermissions = false;
}
function postRun() {
if ((ENVIRONMENT_IS_PTHREAD)) {
return;
}
// PThreads reuse the runtime from the main thread.
if (Module["postRun"]) {
if (typeof Module["postRun"] == "function") Module["postRun"] = [ Module["postRun"] ];
while (Module["postRun"].length) {
addOnPostRun(Module["postRun"].shift());
}
}
// Begin ATPOSTRUNS hooks
callRuntimeCallbacks(onPostRuns);
}
/** @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);
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.
throw e;
}
var wasmBinaryFile;
function findWasmBinary() {
if (Module["locateFile"]) {
return locateFile("scran.wasm");
}
// Use bundler-friendly `new URL(..., import.meta.url)` pattern; works in browsers too.
return new URL("scran.wasm", import.meta.url).href;
}
function getBinarySync(file) {
if (file == wasmBinaryFile && wasmBinary) {
return new Uint8Array(wasmBinary);
}
if (readBinary) {
return readBinary(file);
}
// Throwing a plain string here, even though it not normally adviables since
// this gets turning into an `abort` in instantiateArrayBuffer.
throw "both async and sync fetching of the wasm failed";
}
async function getWasmBinary(binaryFile) {
// If we don't have the binary yet, load it asynchronously using readAsync.
if (!wasmBinary) {
// Fetch the binary using readAsync
try {
var response = await readAsync(binaryFile);
return new Uint8Array(response);
} catch {}
}
// Otherwise, getBinarySync should be able to get it synchronously
return getBinarySync(binaryFile);
}
async function instantiateArrayBuffer(binaryFile, imports) {
try {
var binary = await getWasmBinary(binaryFile);
var instance = await WebAssembly.instantiate(binary, imports);
return instance;
} catch (reason) {
err(`failed to asynchronously prepare wasm: ${reason}`);
abort(reason);
}
}
async function instantiateAsync(binary, binaryFile, imports) {
if (!binary && !ENVIRONMENT_IS_NODE) {
try {
var response = fetch(binaryFile, {
credentials: "same-origin"
});
var instantiationResult = await WebAssembly.instantiateStreaming(response, imports);
return instantiationResult;
} catch (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);
}
function getWasmImports() {
assignWasmImports();
// prepare imports
var imports = {
"a": wasmImports
};
return imports;
}
// Create the wasm instance.
// Receives the wasm imports, returns the exports.
async function createWasm() {
// 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;
wasmExports = applySignatureConversions(wasmExports);
registerTLSInit(wasmExports["za"]);
assignWasmExports(wasmExports);
// We now have the Wasm module loaded up, keep a reference to the compiled module so we can post it to the workers.
wasmModule = module;
return wasmExports;
}
// 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
return receiveInstance(result["instance"], result["module"]);
}
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
// performing.
// Also pthreads and wasm workers initialize the wasm instance through this
// path.
if (Module["instantiateWasm"]) {
return new Promise((resolve, reject) => {
Module["instantiateWasm"](info, (inst, mod) => {
resolve(receiveInstance(inst, mod));
});
});
}
if ((ENVIRONMENT_IS_PTHREAD)) {
// Instantiate from the module that was recieved via postMessage from
// the main thread. We can just use sync instantiation in the worker.
var instance = new WebAssembly.Instance(wasmModule, getWasmImports());
return receiveInstance(instance, wasmModule);
}
wasmBinaryFile ??= findWasmBinary();
var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info);
var exports = receiveInstantiationResult(result);
return exports;
}
// end include: preamble.js
// Begin JS library code
class ExitStatus {
name="ExitStatus";
constructor(status) {
this.message = `Program terminated with exit(${status})`;
this.status = status;
}
}
var terminateWorker = worker => {
worker.terminate();
// terminate() can be asynchronous, so in theory the worker can continue
// to run for some amount of time after termination. However from our POV
// the worker now dead and we don't want to hear from it again, so we stub
// out its message handler here. This avoids having to check in each of
// the onmessage handlers if the message was coming from valid worker.
worker.onmessage = e => {};
};
var cleanupThread = pthread_ptr => {
var worker = PThread.pthreads[pthread_ptr];
PThread.returnWorkerToPool(worker);
};
var callRuntimeCallbacks = callbacks => {
while (callbacks.length > 0) {
// Pass the module as the first argument.
callbacks.shift()(Module);
}
};
var onPreRuns = [];
var addOnPreRun = cb => onPreRuns.push(cb);
var runDependencies = 0;
var dependenciesFulfilled = null;
var removeRunDependency = id => {
runDependencies--;
Module["monitorRunDependencies"]?.(runDependencies);
if (runDependencies == 0) {
if (dependenciesFulfilled) {
var callback = dependenciesFulfilled;
dependenciesFulfilled = null;
callback();
}
}
};
var addRunDependency = id => {
runDependencies++;
Module["monitorRunDependencies"]?.(runDependencies);
};
var spawnThread = threadParams => {
var worker = PThread.getNewWorker();
if (!worker) {
// No available workers in the PThread pool.
return 6;
}
PThread.runningWorkers.push(worker);
// Add to pthreads map
PThread.pthreads[threadParams.pthread_ptr] = worker;
worker.pthread_ptr = threadParams.pthread_ptr;
var msg = {
cmd: "run",
start_routine: threadParams.startRoutine,
arg: threadParams.arg,
pthread_ptr: threadParams.pthread_ptr
};
if (ENVIRONMENT_IS_NODE) {
// Mark worker as weakly referenced once we start executing a pthread,
// so that its existence does not prevent Node.js from exiting. This
// has no effect if the worker is already weakly referenced (e.g. if
// this worker was previously idle/unused).
worker.unref();
}
// Ask the worker to start executing its pthread entry point function.
worker.postMessage(msg, threadParams.transferList);
return 0;
};
var runtimeKeepaliveCounter = 0;
var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0;
var stackSave = () => _emscripten_stack_get_current();
var stackRestore = val => __emscripten_stack_restore(val);
var stackAlloc = sz => __emscripten_stack_alloc(sz);
/** @type{function(number, (number|boolean), ...number)} */ var proxyToMainThread = (funcIndex, emAsmAddr, sync, ...callArgs) => {
// EM_ASM proxying is done by passing a pointer to the address of the EM_ASM
// content as `emAsmAddr`. JS library proxying is done by passing an index
// into `proxiedJSCallArgs` as `funcIndex`. If `emAsmAddr` is non-zero then
// `funcIndex` will be ignored.
// Additional arguments are passed after the first three are the actual
// function arguments.
// The serialization buffer contains the number of call params, and then
// all the args here.
// We also pass 'sync' to C separately, since C needs to look at it.
// Allocate a buffer, which will be copied by the C code.
// First passed parameter specifies the number of arguments to the function.
// When BigInt support is enabled, we must handle types in a more complex
// way, detecting at runtime if a value is a BigInt or not (as we have no
// type info here). To do that, add a "prefix" before each value that
// indicates if it is a BigInt, which effectively doubles the number of
// values we serialize for proxying. TODO: pack this?
var serializedNumCallArgs = callArgs.length * 2;
var sp = stackSave();
var args = stackAlloc(serializedNumCallArgs * 8);
var b = ((args) / 8);
for (var i = 0; i < callArgs.length; i++) {
var arg = callArgs[i];
if (typeof arg == "bigint") {
// The prefix is non-zero to indicate a bigint.
(growMemViews(), HEAP64)[b + 2 * i] = 1n;
(growMemViews(), HEAP64)[b + 2 * i + 1] = arg;
} else {
// The prefix is zero to indicate a JS Number.
(growMemViews(), HEAP64)[b + 2 * i] = 0n;
(growMemViews(), HEAPF64)[b + 2 * i + 1] = arg;
}
}
var rtn = __emscripten_run_js_on_main_thread(funcIndex, emAsmAddr, serializedNumCallArgs, args, sync);
stackRestore(sp);
return rtn;
};
function _proc_exit(code) {
if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(0, 0, 1, code);
EXITSTATUS = code;
if (!keepRuntimeAlive()) {
PThread.terminateAllThreads();
Module["onExit"]?.(code);
ABORT = true;
}
quit_(code, new ExitStatus(code));
}
function exitOnMainThread(returnCode) {
if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(1, 0, 0, returnCode);
_exit(returnCode);
}
/** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => {
EXITSTATUS = status;
if (ENVIRONMENT_IS_PTHREAD) {
// implicit exit can never happen on a pthread
// When running in a pthread we propagate the exit back to the main thread
// where it can decide if the whole process should be shut down or not.
// The pthread may have decided not to exit its own runtime, for example
// because it runs a main loop, but that doesn't affect the main thread.
exitOnMainThread(status);
throw "unwind";
}
_proc_exit(status);
};
var _exit = exitJS;
var PThread = {
unusedWorkers: [],
runningWorkers: [],
tlsInitFunctions: [],
pthreads: {},
init() {
if ((!(ENVIRONMENT_IS_PTHREAD))) {
PThread.initMainThread();
}
},
initMainThread() {
var pthreadPoolSize = Module.scran_custom_nthreads;
// Start loading up the Worker pool, if requested.
while (pthreadPoolSize--) {
PThread.allocateUnusedWorker();
}
// MINIMAL_RUNTIME takes care of calling loadWasmModuleToAllWorkers
// in postamble_minimal.js
addOnPreRun(async () => {
var pthreadPoolReady = PThread.loadWasmModuleToAllWorkers();
addRunDependency("loading-workers");
await pthreadPoolReady;
removeRunDependency("loading-workers");
});
},
terminateAllThreads: () => {
// Attempt to kill all workers. Sadly (at least on the web) there is no
// way to terminate a worker synchronously, or to be notified when a
// worker in actually terminated. This means there is some risk that
// pthreads will continue to be executing after `worker.terminate` has
// returned. For this reason, we don't call `returnWorkerToPool` here or
// free the underlying pthread data structures.
for (var worker of PThread.runningWorkers) {
terminateWorker(worker);
}
for (var worker of PThread.unusedWorkers) {
terminateWorker(worker);
}
PThread.unusedWorkers = [];
PThread.runningWorkers = [];
PThread.pthreads = {};
},
returnWorkerToPool: worker => {
// We don't want to run main thread queued calls here, since we are doing
// some operations that leave the worker queue in an invalid state until
// we are completely done (it would be bad if free() ends up calling a
// queued pthread_create which looks at the global data structures we are
// modifying). To achieve that, defer the free() til the very end, when
// we are all done.
var pthread_ptr = worker.pthread_ptr;
delete PThread.pthreads[pthread_ptr];
// Note: worker is intentionally not terminated so the pool can
// dynamically grow.
PThread.unusedWorkers.push(worker);
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1);
// Not a running Worker anymore
// Detach the worker from the pthread object, and return it to the
// worker pool as an unused worker.
worker.pthread_ptr = 0;
// Finally, free the underlying (and now-unused) pthread structure in
// linear memory.
__emscripten_thread_free_data(pthread_ptr);
},
threadInitTLS() {
// Call thread init functions (these are the _emscripten_tls_init for each
// module loaded.
PThread.tlsInitFunctions.forEach(f => f());
},
loadWasmModuleToWorker: worker => new Promise(onFinishedLoading => {
worker.onmessage = e => {
var d = e["data"];
var cmd = d.cmd;
// If this message is intended to a recipient that is not the main
// thread, forward it to the target thread.
if (d.targetThread && d.targetThread != _pthread_self()) {
var targetWorker = PThread.pthreads[d.targetThread];
if (targetWorker) {
targetWorker.postMessage(d, d.transferList);
} else {
err(`Internal error! Worker sent a message "${cmd}" to target pthread ${d.targetThread}, but that thread no longer exists!`);
}
return;
}
if (cmd === "checkMailbox") {
checkMailbox();
} else if (cmd === "spawnThread") {
spawnThread(d);
} else if (cmd === "cleanupThread") {
// cleanupThread needs to be run via callUserCallback since it calls
// back into user code to free thread data. Without this it's possible
// the unwind or ExitStatus exception could escape here.
callUserCallback(() => cleanupThread(d.thread));
} else if (cmd === "loaded") {
worker.loaded = true;
// Check that this worker doesn't have an associated pthread.
if (ENVIRONMENT_IS_NODE && !worker.pthread_ptr) {
// Once worker is loaded & idle, mark it as weakly referenced,
// so that mere existence of a Worker in the pool does not prevent
// Node.js from exiting the app.
worker.unref();
}
onFinishedLoading(worker);
} else if (d.target === "setimmediate") {
// Worker wants to postMessage() to itself to implement setImmediate()
// emulation.
worker.postMessage(d);
} else if (cmd === "uncaughtException") {
// Message handler for Node.js specific out-of-order behavior:
// https://github.com/nodejs/node/issues/59617
// A pthread sent an uncaught exception event. Re-raise it on the main thread.
worker.onerror(d.error);
} else if (cmd === "callHandler") {
Module[d.handler](...d.args);
} else if (cmd) {
// The received message looks like something that should be handled by this message
// handler, (since there is a e.data.cmd field present), but is not one of the
// recognized commands:
err(`worker sent an unknown command ${cmd}`);
}
};
worker.onerror = e => {
var message = "worker sent an error!";
err(`${message} ${e.filename}:${e.lineno}: ${e.message}`);
throw e;
};
if (ENVIRONMENT_IS_NODE) {
worker.on("message", data => worker.onmessage({
data
}));
worker.on("error", e => worker.onerror(e));
}
// When running on a pthread, none of the incoming parameters on the module
// object are present. Proxy known handlers back to the main thread if specified.
var handlers = [];
var knownHandlers = [ "onExit", "onAbort", "print", "printErr" ];
for (var handler of knownHandlers) {
if (Module.propertyIsEnumerable(handler)) {
handlers.push(handler);
}
}
// Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation.
worker.postMessage({
cmd: "load",
handlers,
wasmMemory,
wasmModule
});
}),
async loadWasmModuleToAllWorkers() {
// Instantiation is synchronous in pthreads.
if (ENVIRONMENT_IS_PTHREAD) {
return;
}
let pthreadPoolReady = Promise.all(PThread.unusedWorkers.map(PThread.loadWasmModuleToWorker));
return pthreadPoolReady;
},
allocateUnusedWorker() {
var worker;
// If we're using module output, use bundler-friendly pattern.
if (Module["mainScriptUrlOrBlob"]) {
var pthreadMainJs = Module["mainScriptUrlOrBlob"];
if (typeof pthreadMainJs != "string") {
pthreadMainJs = URL.createObjectURL(pthreadMainJs);
}
worker = new Worker(pthreadMainJs, {
"type": "module",
// This is the way that we signal to the node worker that it is hosting
// a pthread.
"workerData": "em-pthread",
// This is the way that we signal to the Web Worker that it is hosting
// a pthread.
"name": "em-pthread"
});
} else // We need to generate the URL with import.meta.url as the base URL of the JS file
// instead of just using new URL(import.meta.url) because bundler's only recognize
// the first case in their bundling step. The latter ends up producing an invalid
// URL to import from the server (e.g., for webpack the file:// path).
// See https://github.com/webpack/webpack/issues/12638
worker = new Worker(new URL("scran.js", import.meta.url), {
"type": "module",
// This is the way that we signal to the node worker that it is hosting
// a pthread.
"workerData": "em-pthread",
// This is the way that we signal to the Web Worker that it is hosting
// a pthread.
"name": "em-pthread"
});
PThread.unusedWorkers.push(worker);
},
getNewWorker() {
if (PThread.unusedWorkers.length == 0) {
// PTHREAD_POOL_SIZE_STRICT should show a warning and, if set to level `2`, return from the function.
PThread.allocateUnusedWorker();
PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0]);
}
return PThread.unusedWorkers.pop();
}
};
var onPostRuns = [];
var addOnPostRun = cb => onPostRuns.push(cb);
function establishStackSpace(pthread_ptr) {
var stackHigh = Number((growMemViews(), HEAPU64)[(((pthread_ptr) + (88)) / 8)]);
var stackSize = Number((growMemViews(), HEAPU64)[(((pthread_ptr) + (96)) / 8)]);
var stackLow = stackHigh - stackSize;
// Set stack limits used by `emscripten/stack.h` function. These limits are
// cached in wasm-side globals to make checks as fast as possible.
_emscripten_stack_set_limits(stackHigh, stackLow);
// Call inside wasm module to set up the stack frame for this pthread in wasm module scope
stackRestore(stackHigh);
}
var wasmTableMirror = [];
var getWasmTableEntry = funcPtr => {
// Function pointers should show up as numbers, even under wasm64, but
// we still have some places where bigint values can flow here.
// https://github.com/emscripten-core/emscripten/issues/18200
funcPtr = Number(funcPtr);
var func = wasmTableMirror[funcPtr];
if (!func) {
/** @suppress {checkTypes} */ wasmTableMirror[funcPtr] = func = wasmTable.get(BigInt(funcPtr));
}
return func;
};
var invokeEntryPoint = (ptr, arg) => {
// An old thread on this worker may have been canceled without returning the
// `runtimeKeepaliveCounter` to zero. Reset it now so the new thread won't
// be affected.
runtimeKeepaliveCounter = 0;
// Same for noExitRuntime. The default for pthreads should always be false
// otherwise pthreads would never complete and attempts to pthread_join to
// them would block forever.
// pthreads can still choose to set `noExitRuntime` explicitly, or
// call emscripten_unwind_to_js_event_loop to extend their lifetime beyond
// their main function. See comment in src/runtime_pthread.js for more.
noExitRuntime = 0;
// pthread entry points are always of signature 'void *ThreadMain(void *arg)'
// Native codebases sometimes spawn threads with other thread entry point
// signatures, such as void ThreadMain(void *arg), void *ThreadMain(), or
// void ThreadMain(). That is not acceptable per C/C++ specification, but
// x86 compiler ABI extensions enable that to work. If you find the
// following line to crash, either change the signature to "proper" void
// *ThreadMain(void *arg) form, or try linking with the Emscripten linker
// flag -sEMULATE_FUNCTION_POINTER_CASTS to add in emulation for this x86
// ABI extension.
var result = (a1 => getWasmTableEntry(ptr).call(null, BigInt(a1)))(arg);
function finish(result) {
// In MINIMAL_RUNTIME the noExitRuntime concept does not apply to
// pthreads. To exit a pthread with live runtime, use the function
// emscripten_unwind_to_js_event_loop() in the pthread body.
if (keepRuntimeAlive()) {
EXITSTATUS = result;
return;
}
__emscripten_thread_exit(result);
}
finish(result);
};
var noExitRuntime = true;
var registerTLSInit = tlsInitFunc => PThread.tlsInitFunctions.push(tlsInitFunc);
var wasmMemory;
class ExceptionInfo {
// excPtr - Thrown object pointer to wrap. Metadata pointer is calculated from it.
constructor(excPtr) {
this.excPtr = excPtr;
this.ptr = excPtr - 48;
}
set_type(type) {
(growMemViews(), HEAPU64)[(((this.ptr) + (8)) / 8)] = BigInt(type);
}
get_type() {
return Number((growMemViews(), HEAPU64)[(((this.ptr) + (8)) / 8)]);
}
set_destructor(destructor) {
(growMemViews(), HEAPU64)[(((this.ptr) + (16)) / 8)] = BigInt(destructor);
}
get_destructor() {
return Number((growMemViews(), HEAPU64)[(((this.ptr) + (16)) / 8)]);
}
set_caught(caught) {
caught = caught ? 1 : 0;
(growMemViews(), HEAP8)[(this.ptr) + (24)] = caught;
}
get_caught() {
return (growMemViews(), HEAP8)[(this.ptr) + (24)] != 0;
}
set_rethrown(rethrown) {
rethrown = rethrown ? 1 : 0;
(growMemViews(), HEAP8)[(this.ptr) + (25)] = rethrown;
}
get_rethrown() {
return (growMemViews(), HEAP8)[(this.ptr) + (25)] != 0;
}
// Initialize native structure fields. Should be called once after allocated.
init(type, destructor) {
this.set_adjusted_ptr(0);
this.set_type(type);
this.set_destructor(destructor);
}
set_adjusted_ptr(adjustedPtr) {
(growMemViews(), HEAPU64)[(((this.ptr) + (32)) / 8)] = BigInt(adjustedPtr);
}
get_adjusted_ptr() {
return Number((growMemViews(), HEAPU64)[(((this.ptr) + (32)) / 8)]);
}
}
var exceptionLast = 0;
var uncaughtExceptionCount = 0;
var INT53_MAX = 9007199254740992;
var INT53_MIN = -9007199254740992;
var bigintToI53Checked = num => (num < INT53_MIN || num > INT53_MAX) ? NaN : Number(num);
function ___cxa_throw(ptr, type, destructor) {
ptr = bigintToI53Checked(ptr);
type = bigintToI53Checked(type);
destructor = bigintToI53Checked(destructor);
var info = new ExceptionInfo(ptr);
// Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception.
info.init(type, destructor);
exceptionLast = ptr;
uncaughtExceptionCount++;
throw exceptionLast;
}
function pthreadCreateProxied(pthread_ptr, attr, startRoutine, arg) {
if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(2, 0, 1, pthread_ptr, attr, startRoutine, arg);
return ___pthread_create_js(pthread_ptr, attr, startRoutine, arg);
}
var _emscripten_has_threading_support = () => !!globalThis.SharedArrayBuffer;
function ___pthread_create_js(pthread_ptr, attr, startRoutine, arg) {
pthread_ptr = bigintToI53Checked(pthread_ptr);
attr = bigintToI53Checked(attr);
startRoutine = bigintToI53Checked(startRoutine);
arg = bigintToI53Checked(arg);
if (!_emscripten_has_threading_support()) {
return 6;
}
// List of JS objects that will transfer ownership to the Worker hosting the thread
var transferList = [];
var error = 0;
// Synchronously proxy the thread creation to main thread if possible. If we
// need to transfer ownership of objects, then proxy asynchronously via
// postMessage.
if (ENVIRONMENT_IS_PTHREAD && (transferList.length === 0 || error)) {
return pthreadCreateProxied(pthread_ptr, attr, startRoutine, arg);
}
// If on the main thread, and accessing Canvas/OffscreenCanvas failed, abort
// with the detected error.
if (error) return error;
var threadParams = {
startRoutine,
pthread_ptr,
arg,
transferList
};
if (ENVIRONMENT_IS_PTHREAD) {
// The prepopulated pool of web workers that can host pthreads is stored
// in the main JS thread. Therefore if a pthread is attempting to spawn a
// new thread, the thread creation must be deferred to the main JS thread.
threadParams.cmd = "spawnThread";
postMessage(threadParams, transferList);
// When we defer thread creation this way, we have no way to detect thread
// creation synchronously today, so we have to assume success and return 0.
return 0;
}
// We are the main thread, so we have the pthread warmup pool in this
// thread and can fire off JS thread creation directly ourselves.
return spawnThread(threadParams);
}
var nodePath = require("path");
var PATH = {
isAbs: nodePath.isAbsolute,
normalize: nodePath.normalize,
dirname: nodePath.dirname,
basename: nodePath.basename,
join: nodePath.join,
join2: nodePath.join
};
var initRandomFill = () => view => view.set(crypto.getRandomValues(new Uint8Array(view.byteLength)));
var randomFill = view => {
// Lazily init on the first invocation.
(randomFill = initRandomFill())(view);
};
/** @type{{resolve: function(...*)}} */ var PATH_FS = {
resolve: (...paths) => {
paths.unshift(FS.cwd());
return nodePath.posix.resolve(...paths);
},
relative: (from, to) => nodePath.posix.relative(from || FS.cwd(), to || FS.cwd())
};
var UTF8Decoder = globalThis.TextDecoder && new TextDecoder;
var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => {
var maxIdx = idx + maxBytesToRead;
if (ignoreNul) return maxIdx;
// TextDecoder needs to know the byte length in advance, it doesn't stop on
// null terminator by itself.
// As a tiny code save trick, compare idx against maxIdx using a negation,
// so that maxBytesToRead=undefined/NaN means Infinity.
while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx;
return idx;
};
/**
* 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
* @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character.
* @return {string}
*/ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => {
var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul);
// When using conditional TextDecoder, skip it for short strings as the overhead of the native call is not worth it.
if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
return UTF8Decoder.decode(heapOrArray.buffer instanceof ArrayBuffer ? heapOrArray.subarray(idx, endPtr) : heapOrArray.slice(idx, endPtr));
}
var str = "";
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) {
// 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.codePointAt(i);
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);
// Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16.
// We need to manually skip over the second code unit for correct iteration.
i++;
}
}
// Null-terminate the pointer to the buffer.
heap[outIdx] = 0;
return outIdx - startIdx;
};
/** @type {function(string, boolean=, number=)} */ var 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 star