Buckets:
| /** | |
| * @license | |
| * Copyright 2010 The Emscripten Authors | |
| * SPDX-License-Identifier: MIT | |
| */ | |
| //"use strict"; | |
| // An implementation of basic necessary libraries for the web. This integrates | |
| // with a compiled libc and with the rest of the JS runtime. | |
| // | |
| // We search the Library object when there is an external function. If the | |
| // entry in the Library is a function, we insert it. If it is a string, we | |
| // do another lookup in the library (a simple way to write a function once, | |
| // if it can be called by different names). We also allow dependencies, | |
| // using __deps. Initialization code to be run after allocating all | |
| // global constants can be defined by __postset. | |
| // | |
| // Note that the full function name will be '_' + the name in the Library | |
| // object. For convenience, the short name appears here. Note that if you add a | |
| // new function with an '_', it will not be found. | |
| addToLibrary({ | |
| // HEAP definitions are here to help with TypeScript type generation. | |
| $HEAP8__docs: '/** @type {!Int8Array} */', | |
| $HEAP8: undefined, | |
| $HEAPU8__docs: '/** @type {!Uint8Array} */', | |
| $HEAPU8: undefined, | |
| $HEAP16__docs: '/** @type {!Int16Array} */', | |
| $HEAP16: undefined, | |
| $HEAPU16__docs: '/** @type {!Uint16Array} */', | |
| $HEAPU16: undefined, | |
| $HEAP32__docs: '/** @type {!Int32Array} */', | |
| $HEAP32: undefined, | |
| $HEAPU32__docs: '/** @type {!Uint32Array} */', | |
| $HEAPU32: undefined, | |
| $HEAPF32__docs: '/** @type {!Float32Array} */', | |
| $HEAPF32: undefined, | |
| $HEAPF64__docs: '/** @type {!Float64Array} */', | |
| $HEAPF64: undefined, | |
| #if WASM_BIGINT | |
| // BigInt64Array type is not correctly defined in closure | |
| $HEAP64__docs: '/** not-@type {!BigInt64Array} */', | |
| $HEAP64: undefined, | |
| $HEAPU64__docs: '/** not-@type {!BigUint64Array} */', | |
| $HEAPU64: undefined, | |
| #endif | |
| // JS aliases for native stack manipulation functions and tempret handling | |
| $stackSave__deps: ['emscripten_stack_get_current'], | |
| $stackSave: () => _emscripten_stack_get_current(), | |
| $stackRestore__deps: ['_emscripten_stack_restore'], | |
| $stackRestore: (val) => __emscripten_stack_restore(val), | |
| $stackAlloc__deps: ['_emscripten_stack_alloc'], | |
| $stackAlloc: (sz) => __emscripten_stack_alloc(sz), | |
| $getTempRet0__deps: ['_emscripten_tempret_get'], | |
| $getTempRet0: (val) => __emscripten_tempret_get(), | |
| $setTempRet0__deps: ['_emscripten_tempret_set'], | |
| $setTempRet0: (val) => __emscripten_tempret_set(val), | |
| // Aliases that allow legacy names (without leading $) for the | |
| // functions to continue to work in `__deps` entries. | |
| stackAlloc: '$stackAlloc', | |
| stackSave: '$stackSave', | |
| stackRestore: '$stackSave', | |
| setTempRet0: '$setTempRet0', | |
| getTempRet0: '$getTempRet0', | |
| // Assign a name to a given function. This is mostly useful for debugging | |
| // purposes in cases where new functions are created at runtime. | |
| $createNamedFunction: (name, func) => Object.defineProperty(func, 'name', { value: name }), | |
| // This function is referenced *very* early on in some configurations | |
| // (e.g WASM_WORKERS + RUNTIME_DEBUG) so we explictly use a function here | |
| // rather than an arrow function so that it gets hoisted to the top of the | |
| // scope. | |
| $ptrToString: function(ptr) { | |
| #if ASSERTIONS | |
| assert(typeof ptr === 'number', `ptrToString expects a number, got ${typeof ptr}`); | |
| #endif | |
| #if MEMORY64 | |
| // Convert to 64-bit unsigned value. We need to use BigInt here since | |
| // Number cannot represent the full 64-bit range. | |
| if (ptr < 0) ptr = 2n**64n + BigInt(ptr); | |
| #else | |
| // Convert to 32-bit unsigned value | |
| ptr >>>= 0; | |
| #endif | |
| return '0x' + ptr.toString(16).padStart({{{ POINTER_SIZE * 2 }}}, '0'); | |
| }, | |
| $zeroMemory: (ptr, size) => HEAPU8.fill(0, ptr, ptr + size), | |
| #if SAFE_HEAP | |
| // Trivial wrappers around runtime functions that make these symbols available | |
| // to native code. | |
| segfault: '=segfault', | |
| alignfault: '=alignfault', | |
| #endif | |
| // ========================================================================== | |
| // JavaScript <-> C string interop | |
| // ========================================================================== | |
| #if !MINIMAL_RUNTIME | |
| $exitJS__docs: '/** @param {boolean|number=} implicit */', | |
| $exitJS__deps: [ | |
| 'proc_exit', | |
| #if ASSERTIONS || EXIT_RUNTIME | |
| '$keepRuntimeAlive', | |
| #endif | |
| #if PTHREADS | |
| '$exitOnMainThread', | |
| #endif | |
| #if PTHREADS_DEBUG || ASSERTIONS | |
| '$runtimeKeepaliveCounter', | |
| #endif | |
| ], | |
| $exitJS: (status, implicit) => { | |
| EXITSTATUS = status; | |
| #if ASSERTIONS && !EXIT_RUNTIME | |
| checkUnflushedContent(); | |
| #endif // ASSERTIONS && !EXIT_RUNTIME | |
| #if PTHREADS | |
| if (ENVIRONMENT_IS_PTHREAD) { | |
| // implicit exit can never happen on a pthread | |
| #if ASSERTIONS | |
| assert(!implicit); | |
| #endif | |
| #if PTHREADS_DEBUG | |
| dbg(`Pthread ${ptrToString(_pthread_self())} called exit(${status}), posting exitOnMainThread.`); | |
| #endif | |
| // 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'; | |
| } | |
| #if PTHREADS_DEBUG | |
| err(`main thread called exit(${status}): keepRuntimeAlive=${keepRuntimeAlive()} (counter=${runtimeKeepaliveCounter})`); | |
| #endif // PTHREADS_DEBUG | |
| #endif // PTHREADS | |
| #if EXIT_RUNTIME | |
| if (!keepRuntimeAlive()) { | |
| exitRuntime(); | |
| } | |
| #endif | |
| #if ASSERTIONS | |
| // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down | |
| if (keepRuntimeAlive() && !implicit) { | |
| var msg = `program exited (with status: ${status}), but keepRuntimeAlive() is set (counter=${runtimeKeepaliveCounter}) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)`; | |
| #if MODULARIZE | |
| readyPromiseReject?.(msg); | |
| #endif // MODULARIZE | |
| err(msg); | |
| } | |
| #endif // ASSERTIONS | |
| _proc_exit(status); | |
| }, | |
| #endif | |
| #if MINIMAL_RUNTIME | |
| // minimal runtime doesn't do any exit cleanup handling so just | |
| // map exit directly to the lower-level proc_exit syscall. | |
| exit: 'proc_exit', | |
| #else | |
| exit: '$exitJS', | |
| #endif | |
| // Returns a pointer ('p'), which means an i32 on wasm32 and an i64 wasm64 | |
| // We have a separate JS version `getHeapMax()` which can be called directly | |
| // avoiding any wrapper added for wasm64. | |
| emscripten_get_heap_max__deps: ['$getHeapMax'], | |
| emscripten_get_heap_max: () => getHeapMax(), | |
| $getHeapMax: () => | |
| #if ALLOW_MEMORY_GROWTH | |
| #if MEMORY64 == 1 | |
| {{{ MAXIMUM_MEMORY }}}, | |
| #else | |
| // Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate | |
| // full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side | |
| // for any code that deals with heap sizes, which would require special | |
| // casing all heap size related code to treat 0 specially. | |
| {{{ Math.min(MAXIMUM_MEMORY, FOUR_GB - WASM_PAGE_SIZE) }}}, | |
| #endif | |
| #else // no growth | |
| HEAPU8.length, | |
| #endif | |
| #if ABORTING_MALLOC | |
| $abortOnCannotGrowMemory: (requestedSize) => { | |
| #if ASSERTIONS | |
| #if ALLOW_MEMORY_GROWTH | |
| abort(`Cannot enlarge memory arrays to size ${requestedSize} bytes (OOM). If you want malloc to return NULL (0) instead of this abort, do not link with -sABORTING_MALLOC (that is, the default when growth is enabled is to not abort, but you have overridden that)`); | |
| #else // ALLOW_MEMORY_GROWTH | |
| abort(`Cannot enlarge memory arrays to size ${requestedSize} bytes (OOM). Either (1) compile with -sINITIAL_MEMORY=X with X higher than the current value ${HEAP8.length}, (2) compile with -sALLOW_MEMORY_GROWTH which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -sABORTING_MALLOC=0`); | |
| #endif // ALLOW_MEMORY_GROWTH | |
| #else // ASSERTIONS | |
| abort('OOM'); | |
| #endif // ASSERTIONS | |
| }, | |
| #endif // ABORTING_MALLOC | |
| // Grows the wasm memory to the given byte size, and updates the JS views to | |
| // it. Returns 1 on success, 0 on error. | |
| $growMemory: (size) => { | |
| var oldHeapSize = wasmMemory.buffer.byteLength; | |
| var pages = ((size - oldHeapSize + {{{ WASM_PAGE_SIZE - 1 }}}) / {{{ WASM_PAGE_SIZE }}}) | 0; | |
| #if RUNTIME_DEBUG | |
| dbg(`growMemory: ${size} (+${size - oldHeapSize} bytes / ${pages} pages)`); | |
| #endif | |
| try { | |
| // round size grow request up to wasm page size (fixed 64KB per spec) | |
| wasmMemory.grow({{{ toIndexType('pages') }}}); // .grow() takes a delta compared to the previous size | |
| #if !GROWABLE_ARRAYBUFFERS | |
| updateMemoryViews(); | |
| #endif | |
| #if MEMORYPROFILER | |
| if (typeof emscriptenMemoryProfiler != 'undefined') { | |
| emscriptenMemoryProfiler.onMemoryResize(oldHeapSize, wasmMemory.buffer.byteLength); | |
| } | |
| #endif | |
| return 1 /*success*/; | |
| } catch(e) { | |
| #if ASSERTIONS | |
| err(`growMemory: Attempted to grow heap from ${oldHeapSize} bytes to ${size} bytes, but got error: ${e}`); | |
| #endif | |
| } | |
| // implicit 0 return to save code size (caller will cast "undefined" into 0 | |
| // anyhow) | |
| }, | |
| emscripten_resize_heap__deps: [ | |
| #if ABORTING_MALLOC | |
| '$abortOnCannotGrowMemory', | |
| #endif | |
| #if ALLOW_MEMORY_GROWTH | |
| #if ASSERTIONS == 2 | |
| 'emscripten_get_now', | |
| #endif | |
| '$getHeapMax', | |
| '$alignMemory', | |
| '$growMemory', | |
| #endif | |
| ], | |
| emscripten_resize_heap: 'ip', | |
| emscripten_resize_heap: (requestedSize) => { | |
| var oldSize = HEAPU8.length; | |
| #if !MEMORY64 && !CAN_ADDRESS_2GB | |
| // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. | |
| requestedSize >>>= 0; | |
| #endif | |
| #if ALLOW_MEMORY_GROWTH == 0 | |
| #if ABORTING_MALLOC | |
| abortOnCannotGrowMemory(requestedSize); | |
| #else | |
| return false; // malloc will report failure | |
| #endif // ABORTING_MALLOC | |
| #else // ALLOW_MEMORY_GROWTH == 0 | |
| // With multithreaded builds, races can happen (another thread might increase the size | |
| // in between), so return a failure, and let the caller retry. | |
| #if SHARED_MEMORY | |
| if (requestedSize <= oldSize) { | |
| return false; | |
| } | |
| #elif ASSERTIONS | |
| assert(requestedSize > oldSize); | |
| #endif | |
| #if EMSCRIPTEN_TRACING | |
| // Report old layout one last time | |
| _emscripten_trace_report_memory_layout(); | |
| #endif | |
| // Memory resize rules: | |
| // 1. Always increase heap size to at least the requested size, rounded up | |
| // to next page multiple. | |
| // 2a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap | |
| // geometrically: increase the heap size according to | |
| // MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), At most | |
| // overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB). | |
| // 2b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap | |
| // linearly: increase the heap size by at least | |
| // MEMORY_GROWTH_LINEAR_STEP bytes. | |
| // 3. Max size for the heap is capped at 2048MB-WASM_PAGE_SIZE, or by | |
| // MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest | |
| // 4. If we were unable to allocate as much memory, it may be due to | |
| // over-eager decision to excessively reserve due to (3) above. | |
| // Hence if an allocation fails, cut down on the amount of excess | |
| // growth, in an attempt to succeed to perform a smaller allocation. | |
| // A limit is set for how much we can grow. We should not exceed that | |
| // (the wasm binary specifies it, so if we tried, we'd fail anyhow). | |
| var maxHeapSize = getHeapMax(); | |
| if (requestedSize > maxHeapSize) { | |
| #if ASSERTIONS | |
| err(`Cannot enlarge memory, requested ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`); | |
| #endif | |
| #if ABORTING_MALLOC | |
| abortOnCannotGrowMemory(requestedSize); | |
| #else | |
| return false; | |
| #endif | |
| } | |
| // Loop through potential heap size increases. If we attempt a too eager | |
| // reservation that fails, cut down on the attempted size and reserve a | |
| // smaller bump instead. (max 3 times, chosen somewhat arbitrarily) | |
| for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { | |
| #if MEMORY_GROWTH_LINEAR_STEP == -1 | |
| var overGrownHeapSize = oldSize * (1 + {{{ MEMORY_GROWTH_GEOMETRIC_STEP }}} / cutDown); // ensure geometric growth | |
| #if MEMORY_GROWTH_GEOMETRIC_CAP | |
| // but limit overreserving (default to capping at +96MB overgrowth at most) | |
| overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + {{{ MEMORY_GROWTH_GEOMETRIC_CAP }}} ); | |
| #endif | |
| #else | |
| var overGrownHeapSize = oldSize + {{{ MEMORY_GROWTH_LINEAR_STEP }}} / cutDown; // ensure linear growth | |
| #endif | |
| var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), {{{ WASM_PAGE_SIZE }}})); | |
| #if ASSERTIONS == 2 | |
| var t0 = _emscripten_get_now(); | |
| #endif | |
| var replacement = growMemory(newSize); | |
| #if ASSERTIONS == 2 | |
| var t1 = _emscripten_get_now(); | |
| dbg(`Heap resize call from ${oldSize} to ${newSize} took ${(t1 - t0)} msecs. Success: ${!!replacement}`); | |
| #endif | |
| if (replacement) { | |
| #if ASSERTIONS && WASM2JS | |
| err('Warning: Enlarging memory arrays, this is not fast! ' + [oldSize, newSize]); | |
| #endif | |
| #if EMSCRIPTEN_TRACING | |
| traceLogMessage("Emscripten", `Enlarging memory arrays from ${oldSize} to ${newSize}`); | |
| // And now report the new layout | |
| _emscripten_trace_report_memory_layout(); | |
| #endif | |
| return true; | |
| } | |
| } | |
| #if ASSERTIONS | |
| err(`Failed to grow the heap from ${oldSize} bytes to ${newSize} bytes, not enough memory!`); | |
| #endif | |
| #if ABORTING_MALLOC | |
| abortOnCannotGrowMemory(requestedSize); | |
| #else | |
| return false; | |
| #endif | |
| #endif // ALLOW_MEMORY_GROWTH | |
| }, | |
| #if !GROWABLE_ARRAYBUFFERS | |
| // Called after wasm grows memory. At that time we need to update the views. | |
| // Without this notification, we'd need to check the buffer in JS every time | |
| // we return from any wasm, which adds overhead. See | |
| // https://github.com/WebAssembly/WASI/issues/82 | |
| emscripten_notify_memory_growth: (memoryIndex) => { | |
| #if ASSERTIONS | |
| assert(memoryIndex == 0); | |
| #endif | |
| updateMemoryViews(); | |
| }, | |
| #endif | |
| _emscripten_system: (command) => { | |
| #if ENVIRONMENT_MAY_BE_NODE | |
| if (ENVIRONMENT_IS_NODE) { | |
| if (!command) return 1; // shell is available | |
| var cmdstr = UTF8ToString(command); | |
| if (!cmdstr.length) return 0; // this is what glibc seems to do (shell works test?) | |
| var cp = require('node:child_process'); | |
| var ret = cp.spawnSync(cmdstr, [], {shell:true, stdio:'inherit'}); | |
| var _W_EXITCODE = (ret, sig) => ((ret) << 8 | (sig)); | |
| // this really only can happen if process is killed by signal | |
| if (ret.status === null) { | |
| // sadly node doesn't expose such function | |
| var signalToNumber = (sig) => { | |
| // implement only the most common ones, and fallback to SIGINT | |
| switch (sig) { | |
| case 'SIGHUP': return {{{ cDefs.SIGHUP }}}; | |
| case 'SIGQUIT': return {{{ cDefs.SIGQUIT }}}; | |
| case 'SIGFPE': return {{{ cDefs.SIGFPE }}}; | |
| case 'SIGKILL': return {{{ cDefs.SIGKILL }}}; | |
| case 'SIGALRM': return {{{ cDefs.SIGALRM }}}; | |
| case 'SIGTERM': return {{{ cDefs.SIGTERM }}}; | |
| default: return {{{ cDefs.SIGINT }}}; | |
| } | |
| } | |
| return _W_EXITCODE(0, signalToNumber(ret.signal)); | |
| } | |
| return _W_EXITCODE(ret.status, 0); | |
| } | |
| #endif // ENVIRONMENT_MAY_BE_NODE | |
| // int system(const char *command); | |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html | |
| // Can't call external programs. | |
| if (!command) return 0; // no shell available | |
| return -{{{ cDefs.ENOSYS }}}; | |
| }, | |
| // ========================================================================== | |
| // stdlib.h | |
| // ========================================================================== | |
| #if !STANDALONE_WASM | |
| // Used to implement the native `abort` symbol. Note that we use the | |
| // JavaScript `abort` helper in order to implement this function, but we use a | |
| // distinct name here to avoid confusing the two. | |
| _abort_js: () => | |
| #if ASSERTIONS | |
| abort('native code called abort()'), | |
| #else | |
| abort(''), | |
| #endif | |
| #endif | |
| // This object can be modified by the user during startup, which affects | |
| // the initial values of the environment accessible by getenv. | |
| $ENV: {}, | |
| #if !STANDALONE_WASM | |
| // ========================================================================== | |
| // assert.h | |
| // ========================================================================== | |
| __assert_fail: (condition, filename, line, func) => | |
| abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']), | |
| #endif | |
| #if STACK_OVERFLOW_CHECK >= 2 | |
| // Set stack limits used by binaryen's `StackCheck` pass. | |
| #if MAIN_MODULE | |
| $setStackLimits__deps: ['$setDylinkStackLimits'], | |
| #endif | |
| $setStackLimits: () => { | |
| var stackLow = _emscripten_stack_get_base(); | |
| var stackHigh = _emscripten_stack_get_end(); | |
| #if RUNTIME_DEBUG | |
| dbg(`setStackLimits: ${ptrToString(stackLow)}, ${ptrToString(stackHigh)}`); | |
| #endif | |
| #if MAIN_MODULE | |
| // With dynamic linking we could have any number of pre-loaded libraries | |
| // that each need to have their stack limits set. | |
| setDylinkStackLimits(stackLow, stackHigh); | |
| #else | |
| ___set_stack_limits(stackLow, stackHigh); | |
| #endif | |
| }, | |
| #endif | |
| $withStackSave__deps: ['$stackSave', '$stackRestore'], | |
| $withStackSave: (f) => { | |
| var stack = stackSave(); | |
| var ret = f(); | |
| stackRestore(stack); | |
| return ret; | |
| }, | |
| // ========================================================================== | |
| // setjmp.h | |
| // ========================================================================== | |
| #if SUPPORT_LONGJMP == 'emscripten' | |
| // In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function | |
| // calls that exist in the same function with setjmp are converted to a code | |
| // sequence that includes invokes, malloc, free, saveSetjmp, and | |
| // emscripten_longjmp. setThrew is called from invokes, but we don't have | |
| // any way to express that dependency so we use emscripten_throw_longjmp as | |
| // a proxy and declare the dependency here. | |
| _emscripten_throw_longjmp__deps: ['setThrew'], | |
| _emscripten_throw_longjmp: () => { | |
| throw new EmscriptenSjLj; | |
| }, | |
| #elif !SUPPORT_LONGJMP | |
| #if !INCLUDE_FULL_LIBRARY | |
| // These are in order to print helpful error messages when either longjmp or | |
| // setjmp is used. | |
| longjmp__deps: [() => { | |
| error('longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)'); | |
| }], | |
| get setjmp__deps() { | |
| return this.longjmp__deps; | |
| }, | |
| // This is to print the correct error message when a program is built with | |
| // SUPPORT_LONGJMP=1 but linked with SUPPORT_LONGJMP=0. When a program is | |
| // built with SUPPORT_LONGJMP=1, the object file contains references of not | |
| // longjmp but _emscripten_throw_longjmp, which is called from | |
| // emscripten_longjmp. | |
| get _emscripten_throw_longjmp__deps() { | |
| return this.longjmp__deps; | |
| }, | |
| #endif | |
| _emscripten_throw_longjmp: () => { | |
| error('longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)'); | |
| }, | |
| // will never be emitted, as the dep errors at compile time | |
| longjmp: (env, value) => { | |
| abort('longjmp not supported (build with -s SUPPORT_LONGJMP)'); | |
| }, | |
| setjmp: (env) => { | |
| abort('setjmp not supported (build with -s SUPPORT_LONGJMP)'); | |
| }, | |
| #endif | |
| // ========================================================================== | |
| // errno.h | |
| // ========================================================================== | |
| // We use a string literal here to avoid the string quotes on the object | |
| // keys being removed when processed by jsifier. | |
| $ERRNO_CODES: `{ | |
| 'EPERM': {{{ cDefs.EPERM }}}, | |
| 'ENOENT': {{{ cDefs.ENOENT }}}, | |
| 'ESRCH': {{{ cDefs.ESRCH }}}, | |
| 'EINTR': {{{ cDefs.EINTR }}}, | |
| 'EIO': {{{ cDefs.EIO }}}, | |
| 'ENXIO': {{{ cDefs.ENXIO }}}, | |
| 'E2BIG': {{{ cDefs.E2BIG }}}, | |
| 'ENOEXEC': {{{ cDefs.ENOEXEC }}}, | |
| 'EBADF': {{{ cDefs.EBADF }}}, | |
| 'ECHILD': {{{ cDefs.ECHILD }}}, | |
| 'EAGAIN': {{{ cDefs.EAGAIN }}}, | |
| 'EWOULDBLOCK': {{{ cDefs.EWOULDBLOCK }}}, | |
| 'ENOMEM': {{{ cDefs.ENOMEM }}}, | |
| 'EACCES': {{{ cDefs.EACCES }}}, | |
| 'EFAULT': {{{ cDefs.EFAULT }}}, | |
| 'ENOTBLK': {{{ cDefs.ENOTBLK }}}, | |
| 'EBUSY': {{{ cDefs.EBUSY }}}, | |
| 'EEXIST': {{{ cDefs.EEXIST }}}, | |
| 'EXDEV': {{{ cDefs.EXDEV }}}, | |
| 'ENODEV': {{{ cDefs.ENODEV }}}, | |
| 'ENOTDIR': {{{ cDefs.ENOTDIR }}}, | |
| 'EISDIR': {{{ cDefs.EISDIR }}}, | |
| 'EINVAL': {{{ cDefs.EINVAL }}}, | |
| 'ENFILE': {{{ cDefs.ENFILE }}}, | |
| 'EMFILE': {{{ cDefs.EMFILE }}}, | |
| 'ENOTTY': {{{ cDefs.ENOTTY }}}, | |
| 'ETXTBSY': {{{ cDefs.ETXTBSY }}}, | |
| 'EFBIG': {{{ cDefs.EFBIG }}}, | |
| 'ENOSPC': {{{ cDefs.ENOSPC }}}, | |
| 'ESPIPE': {{{ cDefs.ESPIPE }}}, | |
| 'EROFS': {{{ cDefs.EROFS }}}, | |
| 'EMLINK': {{{ cDefs.EMLINK }}}, | |
| 'EPIPE': {{{ cDefs.EPIPE }}}, | |
| 'EDOM': {{{ cDefs.EDOM }}}, | |
| 'ERANGE': {{{ cDefs.ERANGE }}}, | |
| 'ENOMSG': {{{ cDefs.ENOMSG }}}, | |
| 'EIDRM': {{{ cDefs.EIDRM }}}, | |
| 'ECHRNG': {{{ cDefs.ECHRNG }}}, | |
| 'EL2NSYNC': {{{ cDefs.EL2NSYNC }}}, | |
| 'EL3HLT': {{{ cDefs.EL3HLT }}}, | |
| 'EL3RST': {{{ cDefs.EL3RST }}}, | |
| 'ELNRNG': {{{ cDefs.ELNRNG }}}, | |
| 'EUNATCH': {{{ cDefs.EUNATCH }}}, | |
| 'ENOCSI': {{{ cDefs.ENOCSI }}}, | |
| 'EL2HLT': {{{ cDefs.EL2HLT }}}, | |
| 'EDEADLK': {{{ cDefs.EDEADLK }}}, | |
| 'ENOLCK': {{{ cDefs.ENOLCK }}}, | |
| 'EBADE': {{{ cDefs.EBADE }}}, | |
| 'EBADR': {{{ cDefs.EBADR }}}, | |
| 'EXFULL': {{{ cDefs.EXFULL }}}, | |
| 'ENOANO': {{{ cDefs.ENOANO }}}, | |
| 'EBADRQC': {{{ cDefs.EBADRQC }}}, | |
| 'EBADSLT': {{{ cDefs.EBADSLT }}}, | |
| 'EDEADLOCK': {{{ cDefs.EDEADLOCK }}}, | |
| 'EBFONT': {{{ cDefs.EBFONT }}}, | |
| 'ENOSTR': {{{ cDefs.ENOSTR }}}, | |
| 'ENODATA': {{{ cDefs.ENODATA }}}, | |
| 'ETIME': {{{ cDefs.ETIME }}}, | |
| 'ENOSR': {{{ cDefs.ENOSR }}}, | |
| 'ENONET': {{{ cDefs.ENONET }}}, | |
| 'ENOPKG': {{{ cDefs.ENOPKG }}}, | |
| 'EREMOTE': {{{ cDefs.EREMOTE }}}, | |
| 'ENOLINK': {{{ cDefs.ENOLINK }}}, | |
| 'EADV': {{{ cDefs.EADV }}}, | |
| 'ESRMNT': {{{ cDefs.ESRMNT }}}, | |
| 'ECOMM': {{{ cDefs.ECOMM }}}, | |
| 'EPROTO': {{{ cDefs.EPROTO }}}, | |
| 'EMULTIHOP': {{{ cDefs.EMULTIHOP }}}, | |
| 'EDOTDOT': {{{ cDefs.EDOTDOT }}}, | |
| 'EBADMSG': {{{ cDefs.EBADMSG }}}, | |
| 'ENOTUNIQ': {{{ cDefs.ENOTUNIQ }}}, | |
| 'EBADFD': {{{ cDefs.EBADFD }}}, | |
| 'EREMCHG': {{{ cDefs.EREMCHG }}}, | |
| 'ELIBACC': {{{ cDefs.ELIBACC }}}, | |
| 'ELIBBAD': {{{ cDefs.ELIBBAD }}}, | |
| 'ELIBSCN': {{{ cDefs.ELIBSCN }}}, | |
| 'ELIBMAX': {{{ cDefs.ELIBMAX }}}, | |
| 'ELIBEXEC': {{{ cDefs.ELIBEXEC }}}, | |
| 'ENOSYS': {{{ cDefs.ENOSYS }}}, | |
| 'ENOTEMPTY': {{{ cDefs.ENOTEMPTY }}}, | |
| 'ENAMETOOLONG': {{{ cDefs.ENAMETOOLONG }}}, | |
| 'ELOOP': {{{ cDefs.ELOOP }}}, | |
| 'EOPNOTSUPP': {{{ cDefs.EOPNOTSUPP }}}, | |
| 'EPFNOSUPPORT': {{{ cDefs.EPFNOSUPPORT }}}, | |
| 'ECONNRESET': {{{ cDefs.ECONNRESET }}}, | |
| 'ENOBUFS': {{{ cDefs.ENOBUFS }}}, | |
| 'EAFNOSUPPORT': {{{ cDefs.EAFNOSUPPORT }}}, | |
| 'EPROTOTYPE': {{{ cDefs.EPROTOTYPE }}}, | |
| 'ENOTSOCK': {{{ cDefs.ENOTSOCK }}}, | |
| 'ENOPROTOOPT': {{{ cDefs.ENOPROTOOPT }}}, | |
| 'ESHUTDOWN': {{{ cDefs.ESHUTDOWN }}}, | |
| 'ECONNREFUSED': {{{ cDefs.ECONNREFUSED }}}, | |
| 'EADDRINUSE': {{{ cDefs.EADDRINUSE }}}, | |
| 'ECONNABORTED': {{{ cDefs.ECONNABORTED }}}, | |
| 'ENETUNREACH': {{{ cDefs.ENETUNREACH }}}, | |
| 'ENETDOWN': {{{ cDefs.ENETDOWN }}}, | |
| 'ETIMEDOUT': {{{ cDefs.ETIMEDOUT }}}, | |
| 'EHOSTDOWN': {{{ cDefs.EHOSTDOWN }}}, | |
| 'EHOSTUNREACH': {{{ cDefs.EHOSTUNREACH }}}, | |
| 'EINPROGRESS': {{{ cDefs.EINPROGRESS }}}, | |
| 'EALREADY': {{{ cDefs.EALREADY }}}, | |
| 'EDESTADDRREQ': {{{ cDefs.EDESTADDRREQ }}}, | |
| 'EMSGSIZE': {{{ cDefs.EMSGSIZE }}}, | |
| 'EPROTONOSUPPORT': {{{ cDefs.EPROTONOSUPPORT }}}, | |
| 'ESOCKTNOSUPPORT': {{{ cDefs.ESOCKTNOSUPPORT }}}, | |
| 'EADDRNOTAVAIL': {{{ cDefs.EADDRNOTAVAIL }}}, | |
| 'ENETRESET': {{{ cDefs.ENETRESET }}}, | |
| 'EISCONN': {{{ cDefs.EISCONN }}}, | |
| 'ENOTCONN': {{{ cDefs.ENOTCONN }}}, | |
| 'ETOOMANYREFS': {{{ cDefs.ETOOMANYREFS }}}, | |
| 'EUSERS': {{{ cDefs.EUSERS }}}, | |
| 'EDQUOT': {{{ cDefs.EDQUOT }}}, | |
| 'ESTALE': {{{ cDefs.ESTALE }}}, | |
| 'ENOTSUP': {{{ cDefs.ENOTSUP }}}, | |
| 'ENOMEDIUM': {{{ cDefs.ENOMEDIUM }}}, | |
| 'EILSEQ': {{{ cDefs.EILSEQ }}}, | |
| 'EOVERFLOW': {{{ cDefs.EOVERFLOW }}}, | |
| 'ECANCELED': {{{ cDefs.ECANCELED }}}, | |
| 'ENOTRECOVERABLE': {{{ cDefs.ENOTRECOVERABLE }}}, | |
| 'EOWNERDEAD': {{{ cDefs.EOWNERDEAD }}}, | |
| 'ESTRPIPE': {{{ cDefs.ESTRPIPE }}}, | |
| }`, | |
| #if PURE_WASI | |
| $strError: (errno) => errno + '', | |
| #else | |
| $strError__deps: ['strerror', '$UTF8ToString'], | |
| $strError: (errno) => UTF8ToString(_strerror(errno)), | |
| #endif | |
| #if PROXY_POSIX_SOCKETS == 0 | |
| // ========================================================================== | |
| // netdb.h | |
| // ========================================================================== | |
| $inetPton4: (str) => { | |
| var b = str.split('.'); | |
| for (var i = 0; i < 4; i++) { | |
| var tmp = Number(b[i]); | |
| if (isNaN(tmp)) return null; | |
| b[i] = tmp; | |
| } | |
| return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0; | |
| }, | |
| $inetNtop4: (addr) => | |
| (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff), | |
| $inetPton6__deps: ['htons'], | |
| $inetPton6: (str) => { | |
| var words; | |
| var w, offset, z, i; | |
| /* http://home.deds.nl/~aeron/regex/ */ | |
| var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i | |
| var parts = []; | |
| if (!valid6regx.test(str)) { | |
| return null; | |
| } | |
| if (str === "::") { | |
| return [0, 0, 0, 0, 0, 0, 0, 0]; | |
| } | |
| // Z placeholder to keep track of zeros when splitting the string on ":" | |
| if (str.startsWith("::")) { | |
| str = str.replace("::", "Z:"); // leading zeros case | |
| } else { | |
| str = str.replace("::", ":Z:"); | |
| } | |
| if (str.indexOf(".") > 0) { | |
| // parse IPv4 embedded address | |
| str = str.replace(new RegExp('[.]', 'g'), ":"); | |
| words = str.split(":"); | |
| words[words.length-4] = Number(words[words.length-4]) + Number(words[words.length-3])*256; | |
| words[words.length-3] = Number(words[words.length-2]) + Number(words[words.length-1])*256; | |
| words = words.slice(0, words.length-2); | |
| } else { | |
| words = str.split(":"); | |
| } | |
| offset = 0; z = 0; | |
| for (w=0; w < words.length; w++) { | |
| if (typeof words[w] == 'string') { | |
| if (words[w] === 'Z') { | |
| // compressed zeros - write appropriate number of zero words | |
| for (z = 0; z < (8 - words.length+1); z++) { | |
| parts[w+z] = 0; | |
| } | |
| offset = z-1; | |
| } else { | |
| // parse hex field to 16-bit value and write it in network byte-order | |
| parts[w+offset] = _htons(parseInt(words[w],16)); | |
| } | |
| } else { | |
| // parsed IPv4 words | |
| parts[w+offset] = words[w]; | |
| } | |
| } | |
| return [ | |
| (parts[1] << 16) | parts[0], | |
| (parts[3] << 16) | parts[2], | |
| (parts[5] << 16) | parts[4], | |
| (parts[7] << 16) | parts[6] | |
| ]; | |
| }, | |
| $inetNtop6__deps: ['$inetNtop4', 'ntohs'], | |
| $inetNtop6: (ints) => { | |
| // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 | |
| // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses | |
| // 128-bits are split into eight 16-bit words | |
| // stored in network byte order (big-endian) | |
| // | 80 bits | 16 | 32 bits | | |
| // +-----------------------------------------------------------------+ | |
| // | 10 bytes | 2 | 4 bytes | | |
| // +--------------------------------------+--------------------------+ | |
| // + 5 words | 1 | 2 words | | |
| // +--------------------------------------+--------------------------+ | |
| // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) | |
| // +--------------------------------------+----+---------------------+ | |
| // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) | |
| // +--------------------------------------+----+---------------------+ | |
| var str = ""; | |
| var word = 0; | |
| var longest = 0; | |
| var lastzero = 0; | |
| var zstart = 0; | |
| var len = 0; | |
| var i = 0; | |
| var parts = [ | |
| ints[0] & 0xffff, | |
| (ints[0] >> 16), | |
| ints[1] & 0xffff, | |
| (ints[1] >> 16), | |
| ints[2] & 0xffff, | |
| (ints[2] >> 16), | |
| ints[3] & 0xffff, | |
| (ints[3] >> 16) | |
| ]; | |
| // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses | |
| var hasipv4 = true; | |
| var v4part = ""; | |
| // check if the 10 high-order bytes are all zeros (first 5 words) | |
| for (i = 0; i < 5; i++) { | |
| if (parts[i] !== 0) { hasipv4 = false; break; } | |
| } | |
| if (hasipv4) { | |
| // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) | |
| v4part = inetNtop4(parts[6] | (parts[7] << 16)); | |
| // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) | |
| if (parts[5] === -1) { | |
| str = "::ffff:"; | |
| str += v4part; | |
| return str; | |
| } | |
| // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) | |
| if (parts[5] === 0) { | |
| str = "::"; | |
| // special case IPv6 addresses | |
| if (v4part === "0.0.0.0") v4part = ""; // any/unspecified address | |
| if (v4part === "0.0.0.1") v4part = "1";// loopback address | |
| str += v4part; | |
| return str; | |
| } | |
| } | |
| // Handle all other IPv6 addresses | |
| // first run to find the longest contiguous zero words | |
| for (word = 0; word < 8; word++) { | |
| if (parts[word] === 0) { | |
| if (word - lastzero > 1) { | |
| len = 0; | |
| } | |
| lastzero = word; | |
| len++; | |
| } | |
| if (len > longest) { | |
| longest = len; | |
| zstart = word - longest + 1; | |
| } | |
| } | |
| for (word = 0; word < 8; word++) { | |
| if (longest > 1) { | |
| // compress contiguous zeros - to produce "::" | |
| if (parts[word] === 0 && word >= zstart && word < (zstart + longest) ) { | |
| if (word === zstart) { | |
| str += ":"; | |
| if (zstart === 0) str += ":"; //leading zeros case | |
| } | |
| continue; | |
| } | |
| } | |
| // converts 16-bit words from big-endian to little-endian before converting to hex string | |
| str += Number(_ntohs(parts[word] & 0xffff)).toString(16); | |
| str += word < 7 ? ":" : ""; | |
| } | |
| return str; | |
| }, | |
| $readSockaddr__deps: ['$inetNtop4', '$inetNtop6', 'ntohs'], | |
| $readSockaddr: (sa, salen) => { | |
| // family / port offsets are common to both sockaddr_in and sockaddr_in6 | |
| var family = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'i16') }}}; | |
| var port = _ntohs({{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_port, 'u16') }}}); | |
| var addr; | |
| switch (family) { | |
| case {{{ cDefs.AF_INET }}}: | |
| if (salen !== {{{ C_STRUCTS.sockaddr_in.__size__ }}}) { | |
| return { errno: {{{ cDefs.EINVAL }}} }; | |
| } | |
| addr = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'i32') }}}; | |
| addr = inetNtop4(addr); | |
| break; | |
| case {{{ cDefs.AF_INET6 }}}: | |
| if (salen !== {{{ C_STRUCTS.sockaddr_in6.__size__ }}}) { | |
| return { errno: {{{ cDefs.EINVAL }}} }; | |
| } | |
| addr = [ | |
| {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'i32') }}}, | |
| {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'i32') }}}, | |
| {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'i32') }}}, | |
| {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'i32') }}} | |
| ]; | |
| addr = inetNtop6(addr); | |
| break; | |
| default: | |
| return { errno: {{{ cDefs.EAFNOSUPPORT }}} }; | |
| } | |
| return { family: family, addr: addr, port: port }; | |
| }, | |
| $writeSockaddr__docs: '/** @param {number=} addrlen */', | |
| $writeSockaddr__deps: ['$inetPton4', '$inetPton6', '$zeroMemory', 'htons'], | |
| $writeSockaddr: (sa, family, addr, port, addrlen) => { | |
| switch (family) { | |
| case {{{ cDefs.AF_INET }}}: | |
| addr = inetPton4(addr); | |
| zeroMemory(sa, {{{ C_STRUCTS.sockaddr_in.__size__ }}}); | |
| if (addrlen) { | |
| {{{ makeSetValue('addrlen', 0, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; | |
| } | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'family', 'i16') }}}; | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'addr', 'i32') }}}; | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_port, '_htons(port)', 'i16') }}}; | |
| break; | |
| case {{{ cDefs.AF_INET6 }}}: | |
| addr = inetPton6(addr); | |
| zeroMemory(sa, {{{ C_STRUCTS.sockaddr_in6.__size__ }}}); | |
| if (addrlen) { | |
| {{{ makeSetValue('addrlen', 0, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; | |
| } | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_family, 'family', 'i32') }}}; | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'addr[0]', 'i32') }}}; | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'addr[1]', 'i32') }}}; | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'addr[2]', 'i32') }}}; | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'addr[3]', 'i32') }}}; | |
| {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_port, '_htons(port)', 'i16') }}}; | |
| break; | |
| default: | |
| return {{{ cDefs.EAFNOSUPPORT }}}; | |
| } | |
| return 0; | |
| }, | |
| // We can't actually resolve hostnames in the browser, so instead | |
| // we're generating fake IP addresses with lookup_name that we can | |
| // resolve later on with lookup_addr. | |
| // We do the aliasing in 172.29.*.*, giving us 65536 possibilities. | |
| $DNS__deps: ['$inetPton4', '$inetPton6'], | |
| $DNS: { | |
| address_map: { | |
| id: 1, | |
| addrs: {}, | |
| names: {} | |
| }, | |
| lookup_name(name) { | |
| // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one. | |
| var res = inetPton4(name); | |
| if (res !== null) { | |
| return name; | |
| } | |
| res = inetPton6(name); | |
| if (res !== null) { | |
| return name; | |
| } | |
| // See if this name is already mapped. | |
| var addr; | |
| if (DNS.address_map.addrs[name]) { | |
| addr = DNS.address_map.addrs[name]; | |
| } else { | |
| var id = DNS.address_map.id++; | |
| #if ASSERTIONS | |
| assert(id < 65535, 'exceeded max address mappings of 65535'); | |
| #endif | |
| addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00); | |
| DNS.address_map.names[addr] = name; | |
| DNS.address_map.addrs[name] = addr; | |
| } | |
| return addr; | |
| }, | |
| lookup_addr(addr) { | |
| if (DNS.address_map.names[addr]) { | |
| return DNS.address_map.names[addr]; | |
| } | |
| return null; | |
| } | |
| }, | |
| _emscripten_lookup_name__deps: ['$UTF8ToString', '$DNS', '$inetPton4'], | |
| _emscripten_lookup_name: (name) => { | |
| // uint32_t _emscripten_lookup_name(const char *name); | |
| var nameString = UTF8ToString(name); | |
| return inetPton4(DNS.lookup_name(nameString)); | |
| }, | |
| getaddrinfo__deps: ['$DNS', '$inetPton4', '$inetNtop4', '$inetPton6', '$inetNtop6', '$writeSockaddr', 'malloc', 'htonl'], | |
| getaddrinfo__proxy: 'sync', | |
| getaddrinfo: (node, service, hint, out) => { | |
| // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL | |
| // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we | |
| // really should provide a linked list of suitable addrinfo values. | |
| var addrs = []; | |
| var canon = null; | |
| var addr = 0; | |
| var port = 0; | |
| var flags = 0; | |
| var family = {{{ cDefs.AF_UNSPEC }}}; | |
| var type = 0; | |
| var proto = 0; | |
| var ai, last; | |
| function allocaddrinfo(family, type, proto, canon, addr, port) { | |
| var sa, salen, ai; | |
| var errno; | |
| salen = family === {{{ cDefs.AF_INET6 }}} ? | |
| {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : | |
| {{{ C_STRUCTS.sockaddr_in.__size__ }}}; | |
| addr = family === {{{ cDefs.AF_INET6 }}} ? | |
| inetNtop6(addr) : | |
| inetNtop4(addr); | |
| sa = _malloc(salen); | |
| errno = writeSockaddr(sa, family, addr, port); | |
| #if ASSERTIONS | |
| assert(!errno); | |
| #endif | |
| ai = _malloc({{{ C_STRUCTS.addrinfo.__size__ }}}); | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_family, 'family', 'i32') }}}; | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_socktype, 'type', 'i32') }}}; | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_protocol, 'proto', 'i32') }}}; | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_canonname, 'canon', '*') }}}; | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addr, 'sa', '*') }}}; | |
| if (family === {{{ cDefs.AF_INET6 }}}) { | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; | |
| } else { | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; | |
| } | |
| {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_next, '0', 'i32') }}}; | |
| return ai; | |
| } | |
| if (hint) { | |
| flags = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}}; | |
| family = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_family, 'i32') }}}; | |
| type = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_socktype, 'i32') }}}; | |
| proto = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_protocol, 'i32') }}}; | |
| } | |
| if (type && !proto) { | |
| proto = type === {{{ cDefs.SOCK_DGRAM }}} ? {{{ cDefs.IPPROTO_UDP }}} : {{{ cDefs.IPPROTO_TCP }}}; | |
| } | |
| if (!type && proto) { | |
| type = proto === {{{ cDefs.IPPROTO_UDP }}} ? {{{ cDefs.SOCK_DGRAM }}} : {{{ cDefs.SOCK_STREAM }}}; | |
| } | |
| // If type or proto are set to zero in hints we should really be returning multiple addrinfo values, but for | |
| // now default to a TCP STREAM socket so we can at least return a sensible addrinfo given NULL hints. | |
| if (proto === 0) { | |
| proto = {{{ cDefs.IPPROTO_TCP }}}; | |
| } | |
| if (type === 0) { | |
| type = {{{ cDefs.SOCK_STREAM }}}; | |
| } | |
| if (!node && !service) { | |
| return {{{ cDefs.EAI_NONAME }}}; | |
| } | |
| if (flags & ~({{{ cDefs.AI_PASSIVE }}}|{{{ cDefs.AI_CANONNAME }}}|{{{ cDefs.AI_NUMERICHOST }}}| | |
| {{{ cDefs.AI_NUMERICSERV }}}|{{{ cDefs.AI_V4MAPPED }}}|{{{ cDefs.AI_ALL }}}|{{{ cDefs.AI_ADDRCONFIG }}})) { | |
| return {{{ cDefs.EAI_BADFLAGS }}}; | |
| } | |
| if (hint !== 0 && ({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefs.AI_CANONNAME }}}) && !node) { | |
| return {{{ cDefs.EAI_BADFLAGS }}}; | |
| } | |
| if (flags & {{{ cDefs.AI_ADDRCONFIG }}}) { | |
| // TODO | |
| return {{{ cDefs.EAI_NONAME }}}; | |
| } | |
| if (type !== 0 && type !== {{{ cDefs.SOCK_STREAM }}} && type !== {{{ cDefs.SOCK_DGRAM }}}) { | |
| return {{{ cDefs.EAI_SOCKTYPE }}}; | |
| } | |
| if (family !== {{{ cDefs.AF_UNSPEC }}} && family !== {{{ cDefs.AF_INET }}} && family !== {{{ cDefs.AF_INET6 }}}) { | |
| return {{{ cDefs.EAI_FAMILY }}}; | |
| } | |
| if (service) { | |
| service = UTF8ToString(service); | |
| port = parseInt(service, 10); | |
| if (isNaN(port)) { | |
| if (flags & {{{ cDefs.AI_NUMERICSERV }}}) { | |
| return {{{ cDefs.EAI_NONAME }}}; | |
| } | |
| // TODO support resolving well-known service names from: | |
| // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt | |
| return {{{ cDefs.EAI_SERVICE }}}; | |
| } | |
| } | |
| if (!node) { | |
| if (family === {{{ cDefs.AF_UNSPEC }}}) { | |
| family = {{{ cDefs.AF_INET }}}; | |
| } | |
| if ((flags & {{{ cDefs.AI_PASSIVE }}}) === 0) { | |
| if (family === {{{ cDefs.AF_INET }}}) { | |
| addr = _htonl({{{ cDefs.INADDR_LOOPBACK }}}); | |
| } else { | |
| addr = [0, 0, 0, _htonl(1)]; | |
| } | |
| } | |
| ai = allocaddrinfo(family, type, proto, null, addr, port); | |
| {{{ makeSetValue('out', '0', 'ai', '*') }}}; | |
| return 0; | |
| } | |
| // | |
| // try as a numeric address | |
| // | |
| node = UTF8ToString(node); | |
| addr = inetPton4(node); | |
| if (addr !== null) { | |
| // incoming node is a valid ipv4 address | |
| if (family === {{{ cDefs.AF_UNSPEC }}} || family === {{{ cDefs.AF_INET }}}) { | |
| family = {{{ cDefs.AF_INET }}}; | |
| } | |
| else if (family === {{{ cDefs.AF_INET6 }}} && (flags & {{{ cDefs.AI_V4MAPPED }}})) { | |
| addr = [0, 0, _htonl(0xffff), addr]; | |
| family = {{{ cDefs.AF_INET6 }}}; | |
| } else { | |
| return {{{ cDefs.EAI_NONAME }}}; | |
| } | |
| } else { | |
| addr = inetPton6(node); | |
| if (addr !== null) { | |
| // incoming node is a valid ipv6 address | |
| if (family === {{{ cDefs.AF_UNSPEC }}} || family === {{{ cDefs.AF_INET6 }}}) { | |
| family = {{{ cDefs.AF_INET6 }}}; | |
| } else { | |
| return {{{ cDefs.EAI_NONAME }}}; | |
| } | |
| } | |
| } | |
| if (addr != null) { | |
| ai = allocaddrinfo(family, type, proto, node, addr, port); | |
| {{{ makeSetValue('out', '0', 'ai', '*') }}}; | |
| return 0; | |
| } | |
| if (flags & {{{ cDefs.AI_NUMERICHOST }}}) { | |
| return {{{ cDefs.EAI_NONAME }}}; | |
| } | |
| // | |
| // try as a hostname | |
| // | |
| // resolve the hostname to a temporary fake address | |
| node = DNS.lookup_name(node); | |
| addr = inetPton4(node); | |
| if (family === {{{ cDefs.AF_UNSPEC }}}) { | |
| family = {{{ cDefs.AF_INET }}}; | |
| } else if (family === {{{ cDefs.AF_INET6 }}}) { | |
| addr = [0, 0, _htonl(0xffff), addr]; | |
| } | |
| ai = allocaddrinfo(family, type, proto, null, addr, port); | |
| {{{ makeSetValue('out', '0', 'ai', '*') }}}; | |
| return 0; | |
| }, | |
| getnameinfo__deps: ['$DNS', '$readSockaddr', '$stringToUTF8'], | |
| getnameinfo: (sa, salen, node, nodelen, serv, servlen, flags) => { | |
| var info = readSockaddr(sa, salen); | |
| if (info.errno) { | |
| return {{{ cDefs.EAI_FAMILY }}}; | |
| } | |
| var port = info.port; | |
| var addr = info.addr; | |
| var overflowed = false; | |
| if (node && nodelen) { | |
| var lookup; | |
| if ((flags & {{{ cDefs.NI_NUMERICHOST }}}) || !(lookup = DNS.lookup_addr(addr))) { | |
| if (flags & {{{ cDefs.NI_NAMEREQD }}}) { | |
| return {{{ cDefs.EAI_NONAME }}}; | |
| } | |
| } else { | |
| addr = lookup; | |
| } | |
| var numBytesWrittenExclNull = stringToUTF8(addr, node, nodelen); | |
| if (numBytesWrittenExclNull+1 >= nodelen) { | |
| overflowed = true; | |
| } | |
| } | |
| if (serv && servlen) { | |
| port = '' + port; | |
| var numBytesWrittenExclNull = stringToUTF8(port, serv, servlen); | |
| if (numBytesWrittenExclNull+1 >= servlen) { | |
| overflowed = true; | |
| } | |
| } | |
| if (overflowed) { | |
| // Note: even when we overflow, getnameinfo() is specced to write out the truncated results. | |
| return {{{ cDefs.EAI_OVERFLOW }}}; | |
| } | |
| return 0; | |
| }, | |
| // Implement netdb.h protocol entry (getprotoent, getprotobyname, getprotobynumber, setprotoent, endprotoent) | |
| // http://pubs.opengroup.org/onlinepubs/9699919799/functions/getprotobyname.html | |
| // The Protocols object holds our 'fake' protocols 'database'. | |
| $Protocols: { | |
| list: [], | |
| map: {} | |
| }, | |
| setprotoent__deps: ['$Protocols', '$stringToAscii', 'malloc'], | |
| setprotoent: (stayopen) => { | |
| // void setprotoent(int stayopen); | |
| // Allocate and populate a protoent structure given a name, protocol number and array of aliases | |
| function allocprotoent(name, proto, aliases) { | |
| // write name into buffer | |
| var nameBuf = _malloc(name.length + 1); | |
| stringToAscii(name, nameBuf); | |
| // write aliases into buffer | |
| var j = 0; | |
| var length = aliases.length; | |
| var aliasListBuf = _malloc((length + 1) * 4); // Use length + 1 so we have space for the terminating NULL ptr. | |
| for (var i = 0; i < length; i++, j += 4) { | |
| var alias = aliases[i]; | |
| var aliasBuf = _malloc(alias.length + 1); | |
| stringToAscii(alias, aliasBuf); | |
| {{{ makeSetValue('aliasListBuf', 'j', 'aliasBuf', '*') }}}; | |
| } | |
| {{{ makeSetValue('aliasListBuf', 'j', '0', '*') }}}; // Terminating NULL pointer. | |
| // generate protoent | |
| var pe = _malloc({{{ C_STRUCTS.protoent.__size__ }}}); | |
| {{{ makeSetValue('pe', C_STRUCTS.protoent.p_name, 'nameBuf', '*') }}}; | |
| {{{ makeSetValue('pe', C_STRUCTS.protoent.p_aliases, 'aliasListBuf', '*') }}}; | |
| {{{ makeSetValue('pe', C_STRUCTS.protoent.p_proto, 'proto', 'i32') }}}; | |
| return pe; | |
| }; | |
| // Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial | |
| // to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful. | |
| var list = Protocols.list; | |
| var map = Protocols.map; | |
| if (list.length === 0) { | |
| var entry = allocprotoent('tcp', 6, ['TCP']); | |
| list.push(entry); | |
| map['tcp'] = map['6'] = entry; | |
| entry = allocprotoent('udp', 17, ['UDP']); | |
| list.push(entry); | |
| map['udp'] = map['17'] = entry; | |
| } | |
| _setprotoent.index = 0; | |
| }, | |
| endprotoent: () => { | |
| // void endprotoent(void); | |
| // We're not using a real protocol database so we don't do a real close. | |
| }, | |
| getprotoent__deps: ['setprotoent', '$Protocols'], | |
| getprotoent: (number) => { | |
| // struct protoent *getprotoent(void); | |
| // reads the next entry from the protocols 'database' or return NULL if 'eof' | |
| if (_setprotoent.index === Protocols.list.length) { | |
| return 0; | |
| } | |
| var result = Protocols.list[_setprotoent.index++]; | |
| return result; | |
| }, | |
| getprotobyname__deps: ['setprotoent', '$Protocols'], | |
| getprotobyname: (name) => { | |
| // struct protoent *getprotobyname(const char *); | |
| name = UTF8ToString(name); | |
| _setprotoent(true); | |
| var result = Protocols.map[name]; | |
| return result; | |
| }, | |
| getprotobynumber__deps: ['setprotoent', '$Protocols'], | |
| getprotobynumber: (number) => { | |
| // struct protoent *getprotobynumber(int proto); | |
| _setprotoent(true); | |
| var result = Protocols.map[number]; | |
| return result; | |
| }, | |
| // ========================================================================== | |
| // sockets. Note that the implementation assumes all sockets are always | |
| // nonblocking | |
| // ========================================================================== | |
| #if SOCKET_WEBRTC | |
| $Sockets__deps: [ | |
| () => 'var SocketIO = ' + read('../third_party/socket.io.js') + ';\n', | |
| () => 'var Peer = ' + read('../third_party/wrtcp.js') + ';\n' | |
| ], | |
| #endif | |
| $Sockets: { | |
| BUFFER_SIZE: 10*1024, // initial size | |
| MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer | |
| nextFd: 1, | |
| fds: {}, | |
| nextport: 1, | |
| maxport: 65535, | |
| peer: null, | |
| connections: {}, | |
| portmap: {}, | |
| localAddr: 0xfe00000a, // Local address is always 10.0.0.254 | |
| addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, | |
| 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, | |
| 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a] /* 0x0100000a is reserved */ | |
| }, | |
| #endif // PROXY_POSIX_SOCKETS == 0 | |
| $timers: {}, | |
| $clearTimers__internal: true, | |
| $clearTimers: () => { | |
| for (var t of Object.values(timers)) { | |
| clearTimeout(t.id); | |
| } | |
| }, | |
| // Helper function for setitimer that registers timers with the eventloop. | |
| // Timers always fire on the main thread, either directly from JS (here) or | |
| // or when the main thread is busy waiting calling _emscripten_yield. | |
| _setitimer_js__postset: () => addAtExit('clearTimers();'), | |
| _setitimer_js__proxy: 'sync', | |
| _setitimer_js__deps: ['$timers', '$clearTimers', '$callUserCallback', '_emscripten_timeout', 'emscripten_get_now'], | |
| _setitimer_js: (which, timeout_ms) => { | |
| #if RUNTIME_DEBUG | |
| dbg(`setitimer_js ${which} timeout=${timeout_ms}`); | |
| #endif | |
| // First, clear any existing timer. | |
| if (timers[which]) { | |
| clearTimeout(timers[which].id); | |
| delete timers[which]; | |
| } | |
| // A timeout of zero simply cancels the current timeout so we have nothing | |
| // more to do. | |
| if (!timeout_ms) return 0; | |
| var id = setTimeout(() => { | |
| #if ASSERTIONS | |
| assert(which in timers); | |
| #endif | |
| delete timers[which]; | |
| #if RUNTIME_DEBUG | |
| dbg(`itimer fired: ${which}`); | |
| #endif | |
| callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now())); | |
| }, timeout_ms); | |
| timers[which] = { id, timeout_ms }; | |
| return 0; | |
| }, | |
| // Helper for raise() to avoid signature mismatch failures: | |
| // https://github.com/emscripten-core/posixtestsuite/issues/6 | |
| __call_sighandler: (fp, sig) => {{{ makeDynCall('vi', 'fp') }}}(sig), | |
| // ========================================================================== | |
| // emscripten.h | |
| // ========================================================================== | |
| emscripten_run_script: (ptr) => { | |
| {{{ makeEval('eval(UTF8ToString(ptr));') }}} | |
| }, | |
| emscripten_run_script_int__docs: '/** @suppress{checkTypes} */', | |
| emscripten_run_script_int: (ptr) => { | |
| {{{ makeEval('return eval(UTF8ToString(ptr))|0;') }}} | |
| }, | |
| // Mark as `noleakcheck` otherwise lsan will report the last returned string | |
| // as a leak. | |
| emscripten_run_script_string__noleakcheck: true, | |
| emscripten_run_script_string__deps: ['$lengthBytesUTF8', '$stringToUTF8', 'realloc'], | |
| emscripten_run_script_string: (ptr) => { | |
| {{{ makeEval("var s = eval(UTF8ToString(ptr));") }}} | |
| if (s == null) { | |
| return 0; | |
| } | |
| s += ''; | |
| var me = _emscripten_run_script_string; | |
| me.bufferSize = lengthBytesUTF8(s) + 1; | |
| me.buffer = _realloc(me.buffer ?? 0, me.bufferSize) | |
| stringToUTF8(s, me.buffer, me.bufferSize); | |
| return me.buffer; | |
| }, | |
| emscripten_random: () => Math.random(), | |
| emscripten_date_now: () => Date.now(), | |
| emscripten_performance_now: () => performance.now(), | |
| #if PTHREADS && !AUDIO_WORKLET | |
| // Pthreads need their clocks synchronized to the execution of the main | |
| // thread, so, when using them, make sure to adjust all timings to the | |
| // respective time origins. | |
| emscripten_get_now: () => performance.timeOrigin + performance.now(), | |
| #else | |
| #if AUDIO_WORKLET // https://github.com/WebAudio/web-audio-api/issues/2413 | |
| emscripten_get_now: `; | |
| // AudioWorkletGlobalScope does not have performance.now() | |
| // (https://github.com/WebAudio/web-audio-api/issues/2527), so if building | |
| // with | |
| // Audio Worklets enabled, do a dynamic check for its presence. | |
| if (globalThis.performance?.now) { | |
| #if PTHREADS | |
| _emscripten_get_now = () => performance.timeOrigin + performance.now(); | |
| #else | |
| _emscripten_get_now = () => performance.now(); | |
| #endif | |
| } else { | |
| _emscripten_get_now = Date.now; | |
| } | |
| `, | |
| #else | |
| // Modern environment where performance.now() is supported: | |
| // N.B. a shorter form "_emscripten_get_now = performance.now;" is | |
| // unfortunately not allowed even in current browsers (e.g. FF Nightly 75). | |
| emscripten_get_now: () => performance.now(), | |
| #endif | |
| #endif | |
| emscripten_get_now_res: () => { // return resolution of get_now, in nanoseconds | |
| #if ENVIRONMENT_MAY_BE_NODE | |
| if (ENVIRONMENT_IS_NODE) { | |
| return 1; // nanoseconds | |
| } | |
| #endif | |
| #if AUDIO_WORKLET // https://github.com/WebAudio/web-audio-api/issues/2413 | |
| if (globalThis.performance?.now == 'function') { | |
| return 1000; // microseconds (1/1000 of a millisecond) | |
| } | |
| return 1000*1000; // milliseconds | |
| #else | |
| // Modern environment where performance.now() is supported: | |
| return 1000; // microseconds (1/1000 of a millisecond) | |
| #endif | |
| }, | |
| // Represents whether emscripten_get_now is guaranteed monotonic; the Date.now | |
| // implementation is not :( | |
| $nowIsMonotonic__internal: true, | |
| #if AUDIO_WORKLET // // https://github.com/WebAudio/web-audio-api/issues/2413 | |
| $nowIsMonotonic: `!!globalThis.performance?.now;`, | |
| #else | |
| // Modern environment where performance.now() is supported | |
| $nowIsMonotonic: 1, | |
| #endif | |
| _emscripten_get_now_is_monotonic__internal: true, | |
| _emscripten_get_now_is_monotonic__deps: ['$nowIsMonotonic'], | |
| _emscripten_get_now_is_monotonic: () => nowIsMonotonic, | |
| $warnOnce: (text) => { | |
| warnOnce.shown ||= {}; | |
| if (!warnOnce.shown[text]) { | |
| warnOnce.shown[text] = 1; | |
| #if ENVIRONMENT_MAY_BE_NODE | |
| if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text; | |
| #endif | |
| err(text); | |
| } | |
| }, | |
| _emscripten_log_formatted__deps: ['$getCallstack'], | |
| _emscripten_log_formatted: (flags, str) => { | |
| str = UTF8ToString(str); | |
| if (flags & {{{ cDefs.EM_LOG_C_STACK | cDefs.EM_LOG_JS_STACK }}}) { | |
| str = str.replace(/\s+$/, ''); // Ensure the message and the callstack are joined cleanly with exactly one newline. | |
| str += (str.length > 0 ? '\n' : '') + getCallstack(flags); | |
| } | |
| if (flags & {{{ cDefs.EM_LOG_CONSOLE }}}) { | |
| if (flags & {{{ cDefs.EM_LOG_ERROR }}}) { | |
| console.error(str); | |
| } else if (flags & {{{ cDefs.EM_LOG_WARN }}}) { | |
| console.warn(str); | |
| } else if (flags & {{{ cDefs.EM_LOG_INFO }}}) { | |
| console.info(str); | |
| } else if (flags & {{{ cDefs.EM_LOG_DEBUG }}}) { | |
| console.debug(str); | |
| } else { | |
| console.log(str); | |
| } | |
| } else if (flags & {{{ cDefs.EM_LOG_ERROR | cDefs.EM_LOG_WARN }}}) { | |
| err(str); | |
| } else { | |
| out(str); | |
| } | |
| }, | |
| // We never free the return values of this function so we need to allocate | |
| // using builtin_malloc to avoid LSan reporting these as leaks. | |
| #if RETAIN_COMPILER_SETTINGS | |
| emscripten_get_compiler_setting__noleakcheck: true, | |
| emscripten_get_compiler_setting__deps: ['$stringToNewUTF8'], | |
| emscripten_get_compiler_setting: (name) => { | |
| name = UTF8ToString(name); | |
| var ret = getCompilerSetting(name); | |
| if (typeof ret == 'number' || typeof ret == 'boolean') return ret; | |
| var cache = _emscripten_get_compiler_setting.cache ??= {}; | |
| var fullret = cache[name]; | |
| if (fullret) return fullret; | |
| return cache[name] = stringToNewUTF8(ret); | |
| }, | |
| #else | |
| emscripten_get_compiler_setting: (name) => abort('You must build with -sRETAIN_COMPILER_SETTINGS for getCompilerSetting or emscripten_get_compiler_setting to work'), | |
| #endif | |
| emscripten_has_asyncify: () => {{{ ASYNCIFY }}}, | |
| emscripten_debugger: () => { debugger }, | |
| emscripten_print_double__deps: ['$stringToUTF8', '$lengthBytesUTF8'], | |
| emscripten_print_double: (x, to, max) => { | |
| var str = x + ''; | |
| if (to) return stringToUTF8(str, to, max); | |
| else return lengthBytesUTF8(str); | |
| }, | |
| #if USE_ASAN || USE_LSAN | |
| // When lsan is enabled noLeakCheck will temporarily disable leak checking | |
| // for the duration of the function. | |
| $noLeakCheck__deps: ['__lsan_enable', '__lsan_disable'], | |
| $noLeakCheck__docs: '/** @suppress{checkTypes} */', | |
| $noLeakCheck: (func) => { | |
| if (runtimeInitialized) ___lsan_disable(); | |
| try { | |
| return func(); | |
| } finally { | |
| if (runtimeInitialized) ___lsan_enable(); | |
| } | |
| }, | |
| #endif | |
| #if USE_ASAN || USE_LSAN || UBSAN_RUNTIME | |
| _emscripten_sanitizer_use_colors: () => { | |
| var setting = Module['printWithColors']; | |
| if (setting !== undefined) { | |
| return setting; | |
| } | |
| return ENVIRONMENT_IS_NODE && process.stderr.isTTY; | |
| }, | |
| _emscripten_sanitizer_get_option__deps: ['$stringToNewUTF8', '$UTF8ToString'], | |
| _emscripten_sanitizer_get_option__sig: 'pp', | |
| _emscripten_sanitizer_get_option: (name) => stringToNewUTF8(Module[UTF8ToString(name)] || ''), | |
| #endif | |
| $readEmAsmArgsArray: [], | |
| $readEmAsmArgs__deps: ['$readEmAsmArgsArray'], | |
| $readEmAsmArgs: (sigPtr, buf) => { | |
| #if ASSERTIONS | |
| // Nobody should have mutated _readEmAsmArgsArray underneath us to be something else than an array. | |
| assert(Array.isArray(readEmAsmArgsArray)); | |
| // The input buffer is allocated on the stack, so it must be stack-aligned. | |
| assert(buf % {{{ STACK_ALIGN }}} == 0); | |
| #endif | |
| readEmAsmArgsArray.length = 0; | |
| var ch; | |
| // Most arguments are i32s, so shift the buffer pointer so it is a plain | |
| // index into HEAP32. | |
| while (ch = HEAPU8[sigPtr++]) { | |
| #if ASSERTIONS | |
| var chr = String.fromCharCode(ch); | |
| var validChars = ['d', 'f', 'i', 'p']; | |
| #if WASM_BIGINT | |
| // In WASM_BIGINT mode we support passing i64 values as bigint. | |
| validChars.push('j'); | |
| #endif | |
| assert(validChars.includes(chr), `Invalid character ${ch}("${chr}") in readEmAsmArgs! Use only [${validChars}], and do not specify "v" for void return argument.`); | |
| #endif | |
| // Floats are always passed as doubles, so all types except for 'i' | |
| // are 8 bytes and require alignment. | |
| var wide = (ch != {{{ charCode('i') }}}); | |
| #if !MEMORY64 | |
| wide &= (ch != {{{ charCode('p') }}}); | |
| #endif | |
| buf += wide && (buf % 8) ? 4 : 0; | |
| readEmAsmArgsArray.push( | |
| // Special case for pointers under wasm64 or CAN_ADDRESS_2GB mode. | |
| ch == {{{ charCode('p') }}} ? {{{ makeGetValue('buf', 0, '*') }}} : | |
| #if WASM_BIGINT | |
| ch == {{{ charCode('j') }}} ? {{{ makeGetValue('buf', 0, 'i64') }}} : | |
| #endif | |
| ch == {{{ charCode('i') }}} ? | |
| {{{ makeGetValue('buf', 0, 'i32') }}} : | |
| {{{ makeGetValue('buf', 0, 'double') }}} | |
| ); | |
| buf += wide ? 8 : 4; | |
| } | |
| return readEmAsmArgsArray; | |
| }, | |
| #if HAVE_EM_ASM | |
| $runEmAsmFunction__deps: ['$readEmAsmArgs'], | |
| $runEmAsmFunction: (code, sigPtr, argbuf) => { | |
| var args = readEmAsmArgs(sigPtr, argbuf); | |
| #if ASSERTIONS | |
| assert(ASM_CONSTS.hasOwnProperty(code), `No EM_ASM constant found at address ${code}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); | |
| #endif | |
| return ASM_CONSTS[code](...args); | |
| }, | |
| emscripten_asm_const_int__deps: ['$runEmAsmFunction'], | |
| emscripten_asm_const_int: (code, sigPtr, argbuf) => { | |
| return runEmAsmFunction(code, sigPtr, argbuf); | |
| }, | |
| emscripten_asm_const_double__deps: ['$runEmAsmFunction'], | |
| emscripten_asm_const_double: (code, sigPtr, argbuf) => { | |
| return runEmAsmFunction(code, sigPtr, argbuf); | |
| }, | |
| emscripten_asm_const_ptr__deps: ['$runEmAsmFunction'], | |
| emscripten_asm_const_ptr: (code, sigPtr, argbuf) => { | |
| return runEmAsmFunction(code, sigPtr, argbuf); | |
| }, | |
| $runMainThreadEmAsm__deps: ['$readEmAsmArgs', | |
| #if PTHREADS | |
| '$proxyToMainThread' | |
| #endif | |
| ], | |
| $runMainThreadEmAsm: (emAsmAddr, sigPtr, argbuf, sync) => { | |
| var args = readEmAsmArgs(sigPtr, argbuf); | |
| #if PTHREADS | |
| if (ENVIRONMENT_IS_PTHREAD) { | |
| // EM_ASM functions are variadic, receiving the actual arguments as a buffer | |
| // in memory. the last parameter (argBuf) points to that data. We need to | |
| // always un-variadify that, *before proxying*, as in the async case this | |
| // is a stack allocation that LLVM made, which may go away before the main | |
| // thread gets the message. For that reason we handle proxying *after* the | |
| // call to readEmAsmArgs, and therefore we do that manually here instead | |
| // of using __proxy. (And for simplicity, do the same in the sync | |
| // case as well, even though it's not strictly necessary, to keep the two | |
| // code paths as similar as possible on both sides.) | |
| return proxyToMainThread(0, emAsmAddr, sync, ...args); | |
| } | |
| #endif | |
| #if ASSERTIONS | |
| assert(ASM_CONSTS.hasOwnProperty(emAsmAddr), `No EM_ASM constant found at address ${emAsmAddr}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); | |
| #endif | |
| return ASM_CONSTS[emAsmAddr](...args); | |
| }, | |
| emscripten_asm_const_int_sync_on_main_thread__deps: ['$runMainThreadEmAsm'], | |
| emscripten_asm_const_int_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1), | |
| emscripten_asm_const_ptr_sync_on_main_thread__deps: ['$runMainThreadEmAsm'], | |
| emscripten_asm_const_ptr_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1), | |
| emscripten_asm_const_double_sync_on_main_thread: 'emscripten_asm_const_int_sync_on_main_thread', | |
| emscripten_asm_const_async_on_main_thread__deps: ['$runMainThreadEmAsm'], | |
| emscripten_asm_const_async_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 0), | |
| #endif | |
| #if !DECLARE_ASM_MODULE_EXPORTS | |
| // When DECLARE_ASM_MODULE_EXPORTS is set, this function is programmatically | |
| // created during linking. See `create_receiving` in `emscripten.py`. | |
| // When DECLARE_ASM_MODULE_EXPORTS=0 is set, `assignWasmExports` is instead | |
| // defined here as a normal JS library function. | |
| $assignWasmExports__deps: ['$asmjsMangle', | |
| #if DYNCALLS || !WASM_BIGINT | |
| , '$dynCalls' | |
| #endif | |
| ], | |
| $assignWasmExports: (wasmExports) => { | |
| for (var [name, exportedSymbol] of Object.entries(wasmExports)) { | |
| name = asmjsMangle(name); | |
| #if DYNCALLS || !WASM_BIGINT | |
| if (name.startsWith('dynCall_')) { | |
| dynCalls[name.substr(8)] = exportedSymbol; | |
| } | |
| #endif | |
| // Special handling for Wasm globals. See `create_receiving` for the | |
| // static version of this code. | |
| if (typeof exportedSymbol.value != 'undefined') { | |
| #if MEMORY64 | |
| exportedSymbol = Number(exportedSymbol.value); | |
| #else | |
| exportedSymbol = exportedSymbol.value | |
| #endif | |
| } | |
| #if MINIMAL_RUNTIME | |
| globalThis[name] = exportedSymbol; | |
| #else | |
| globalThis[name] = Module[name] = exportedSymbol; | |
| #endif | |
| } | |
| exportAliases(wasmExports); | |
| }, | |
| #endif | |
| // Parses as much of the given JS string to an integer, with quiet error | |
| // handling (returns a NaN on error). E.g. jstoi_q("123abc") returns 123. | |
| // Note that "smart" radix handling is employed for input string: | |
| // "0314" is parsed as octal, and "0x1234" is parsed as base-16. | |
| $jstoi_q__docs: '/** @suppress {checkTypes} */', | |
| $jstoi_q: (str) => parseInt(str), | |
| #if LINK_AS_CXX | |
| // libunwind | |
| _Unwind_Backtrace__deps: ['$getCallstack'], | |
| _Unwind_Backtrace: (func, arg) => { | |
| var trace = getCallstack(); | |
| var parts = trace.split('\n'); | |
| for (var i = 0; i < parts.length; i++) { | |
| var ret = {{{ makeDynCall('iii', 'func') }}}(0, arg); | |
| if (ret !== 0) return; | |
| } | |
| }, | |
| _Unwind_GetIPInfo: (context, ipBefore) => abort('Unwind_GetIPInfo'), | |
| _Unwind_FindEnclosingFunction: (ip) => 0, // we cannot succeed | |
| _Unwind_RaiseException__deps: ['__cxa_throw'], | |
| _Unwind_RaiseException: (ex) => { | |
| err('Warning: _Unwind_RaiseException is not correctly implemented'); | |
| return ___cxa_throw(ex, 0, 0); | |
| }, | |
| _Unwind_DeleteException: (ex) => err('TODO: Unwind_DeleteException'), | |
| #endif | |
| // special runtime support | |
| #if STACK_OVERFLOW_CHECK | |
| // Used by wasm-emscripten-finalize to implement STACK_OVERFLOW_CHECK | |
| __handle_stack_overflow__deps: ['emscripten_stack_get_base', 'emscripten_stack_get_end', '$ptrToString'], | |
| __handle_stack_overflow: (requested) => { | |
| var base = _emscripten_stack_get_base(); | |
| var end = _emscripten_stack_get_end(); | |
| abort(`stack overflow (Attempt to set SP to ${ptrToString(requested)}` + | |
| `, with stack limits [${ptrToString(end)} - ${ptrToString(base)}` + | |
| ']). If you require more stack space build with -sSTACK_SIZE=<bytes>'); | |
| }, | |
| #endif | |
| #if MINIMAL_RUNTIME // MINIMAL_RUNTIME does not have a global runtime variable thisProgram | |
| $getExecutableName: () => { | |
| #if ENVIRONMENT_MAY_BE_NODE | |
| if (ENVIRONMENT_IS_NODE && process.argv.length > 1) { | |
| return process.argv[1].replace(/\\/g, '/'); | |
| } | |
| #endif | |
| return "./this.program"; | |
| }, | |
| #else | |
| $getExecutableName: () => thisProgram || './this.program', | |
| #endif | |
| // Receives a Web Audio context plus a set of elements to listen for user | |
| // input events on, and registers a context resume() for them. This lets | |
| // audio work properly in an automatic way, as browsers won't let audio run | |
| // without user interaction. | |
| $autoResumeAudioContext: (ctx) => { | |
| for (var event of ['keydown', 'mousedown', 'touchstart']) { | |
| for (var element of [document, document.getElementById('canvas')]) { | |
| element?.addEventListener(event, () => { | |
| if (ctx.state === 'suspended') ctx.resume(); | |
| }, { 'once': true }); | |
| } | |
| } | |
| }, | |
| #if DYNCALLS || !WASM_BIGINT | |
| $dynCalls__internal: true, | |
| $dynCalls: {}, | |
| $dynCallLegacy__deps: ['$dynCalls'], | |
| $dynCallLegacy: (sig, ptr, args) => { | |
| sig = sig.replace(/p/g, {{{ MEMORY64 ? "'j'" : "'i'" }}}) | |
| #if ASSERTIONS | |
| assert(sig in dynCalls, `bad function pointer type - sig is not in dynCalls: '${sig}'`); | |
| if (args?.length) { | |
| #if WASM_BIGINT | |
| // j (64-bit integer) is fine, and is implemented as a BigInt. Without | |
| // legalization, the number of parameters should match (j is not expanded | |
| // into two i's). | |
| assert(args.length === sig.length - 1); | |
| #else | |
| // j (64-bit integer) must be passed in as two numbers [low 32, high 32]. | |
| assert(args.length === sig.substring(1).replace(/j/g, '--').length); | |
| #endif | |
| } else { | |
| assert(sig.length == 1); | |
| } | |
| #endif | |
| var f = dynCalls[sig]; | |
| return f(ptr, ...args); | |
| }, | |
| $dynCall__deps: [ | |
| #if DYNCALLS || !WASM_BIGINT | |
| '$dynCallLegacy', | |
| #endif | |
| #if !DYNCALLS | |
| '$getWasmTableEntry', | |
| #endif | |
| ], | |
| #endif | |
| // Used in library code to get JS function from wasm function pointer. | |
| // All callers should use direct table access where possible and only fall | |
| // back to this function if needed. | |
| $getDynCaller__deps: ['$dynCall'], | |
| $getDynCaller: (sig, ptr, promising = false) => { | |
| #if ASSERTIONS && !DYNCALLS | |
| assert(sig.includes('j') || sig.includes('p'), 'getDynCaller should only be called with i64 sigs') | |
| #endif | |
| return (...args) => dynCall(sig, ptr, args, promising); | |
| }, | |
| $dynCall: (sig, ptr, args = [], promising = false) => { | |
| #if ASSERTIONS | |
| assert(ptr, `null function pointer in dynCall`); | |
| #endif | |
| #if ASSERTIONS && (DYNCALLS || !WASM_BIGINT || !JSPI) | |
| assert(!promising, 'async dynCall is not supported in this mode') | |
| #endif | |
| #if MEMORY64 | |
| // With MEMORY64 we have an additional step to convert `p` arguments to | |
| // bigint. This is the runtime equivalent of the wrappers we create for wasm | |
| // exports in `emscripten.py:create_wasm64_wrappers`. | |
| for (var i = 1; i < sig.length; ++i) { | |
| if (sig[i] == 'p') args[i-1] = BigInt(args[i-1]); | |
| } | |
| #endif | |
| #if DYNCALLS | |
| var rtn = dynCallLegacy(sig, ptr, args); | |
| #else | |
| #if !WASM_BIGINT | |
| // Without WASM_BIGINT support we cannot directly call function with i64 as | |
| // part of their signature, so we rely on the dynCall functions generated by | |
| // wasm-emscripten-finalize | |
| if (sig.includes('j')) { | |
| return dynCallLegacy(sig, ptr, args); | |
| } | |
| #endif | |
| #if ASSERTIONS | |
| assert(getWasmTableEntry(ptr), `missing table entry in dynCall: ${ptr}`); | |
| #endif | |
| var func = getWasmTableEntry(ptr); | |
| #if JSPI | |
| if (promising) { | |
| func = WebAssembly.promising(func); | |
| } | |
| #endif | |
| var rtn = func(...args); | |
| #endif // DYNCALLS | |
| function convert(rtn) { | |
| #if MEMORY64 | |
| return sig[0] == 'p' ? Number(rtn) : rtn; | |
| #elif CAN_ADDRESS_2GB | |
| return sig[0] == 'p' ? rtn >>> 0 : rtn; | |
| #else | |
| return rtn; | |
| #endif | |
| } | |
| #if JSPI | |
| if (promising) { | |
| return rtn.then(convert); | |
| } | |
| #endif | |
| return convert(rtn); | |
| }, | |
| $callRuntimeCallbacks__internal: true, | |
| $callRuntimeCallbacks: (callbacks) => { | |
| while (callbacks.length > 0) { | |
| // Pass the module as the first argument. | |
| callbacks.shift()(Module); | |
| } | |
| }, | |
| #if SHRINK_LEVEL == 0 || ASYNCIFY == 2 | |
| // A mirror copy of contents of wasmTable in JS side, to avoid relatively | |
| // slow wasmTable.get() call. Only used when not compiling with -Os, -Oz, or | |
| // JSPI which needs to instrument the functions. | |
| $wasmTableMirror__internal: true, | |
| $wasmTableMirror: [], | |
| $setWasmTableEntry__internal: true, | |
| $setWasmTableEntry__deps: ['$wasmTableMirror', '$wasmTable'], | |
| $setWasmTableEntry: (idx, func) => { | |
| /** @suppress {checkTypes} */ | |
| wasmTable.set({{{ toIndexType('idx') }}}, func); | |
| // With ABORT_ON_WASM_EXCEPTIONS wasmTable.get is overridden to return wrapped | |
| // functions so we need to call it here to retrieve the potential wrapper correctly | |
| // instead of just storing 'func' directly into wasmTableMirror | |
| /** @suppress {checkTypes} */ | |
| wasmTableMirror[idx] = wasmTable.get({{{ toIndexType('idx') }}}); | |
| }, | |
| $getWasmTableEntry__internal: true, | |
| $getWasmTableEntry__deps: ['$wasmTableMirror', '$wasmTable'], | |
| $getWasmTableEntry: (funcPtr) => { | |
| #if MEMORY64 | |
| // 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); | |
| #endif | |
| var func = wasmTableMirror[funcPtr]; | |
| if (!func) { | |
| /** @suppress {checkTypes} */ | |
| wasmTableMirror[funcPtr] = func = wasmTable.get({{{ toIndexType('funcPtr') }}}); | |
| #if ASYNCIFY == 2 | |
| if (Asyncify.isAsyncExport(func)) { | |
| wasmTableMirror[funcPtr] = func = Asyncify.makeAsyncFunction(func); | |
| } | |
| #endif | |
| } | |
| #if ASSERTIONS && ASYNCIFY != 2 // With JSPI the function stored in the table will be a wrapper. | |
| /** @suppress {checkTypes} */ | |
| assert(wasmTable.get({{{ toIndexType('funcPtr') }}}) == func, 'table mirror is out of date'); | |
| #endif | |
| return func; | |
| }, | |
| #else | |
| $setWasmTableEntry__docs: '/** @suppress{checkTypes} */', | |
| $setWasmTableEntry__deps: ['$wasmTable'], | |
| $setWasmTableEntry: (idx, func) => wasmTable.set({{{ toIndexType('idx') }}}, func), | |
| $getWasmTableEntry__docs: '/** @suppress{checkTypes} */', | |
| $getWasmTableEntry__deps: ['$wasmTable'], | |
| $getWasmTableEntry: (funcPtr) => { | |
| // In -Os and -Oz builds, do not implement a JS side wasm table mirror for small | |
| // code size, but directly access wasmTable, which is a bit slower as uncached. | |
| return wasmTable.get({{{ toIndexType('funcPtr') }}}); | |
| }, | |
| #endif // SHRINK_LEVEL == 0 | |
| // Callable in pthread without __proxy needed. | |
| emscripten_exit_with_live_runtime: () => { | |
| {{{ runtimeKeepalivePush() }}} | |
| throw 'unwind'; | |
| }, | |
| #if !MINIMAL_RUNTIME | |
| _emscripten_runtime_keepalive_clear__deps: ['$runtimeKeepaliveCounter'], | |
| #endif | |
| _emscripten_runtime_keepalive_clear: () => { | |
| #if isSymbolNeeded('$noExitRuntime') | |
| noExitRuntime = false; | |
| #endif | |
| #if !MINIMAL_RUNTIME | |
| runtimeKeepaliveCounter = 0; | |
| #endif | |
| }, | |
| emscripten_force_exit__deps: ['exit', '_emscripten_runtime_keepalive_clear', | |
| #if !EXIT_RUNTIME && ASSERTIONS | |
| '$warnOnce', | |
| #endif | |
| ], | |
| emscripten_force_exit__proxy: 'sync', | |
| emscripten_force_exit: (status) => { | |
| #if RUNTIME_DEBUG | |
| dbg('emscripten_force_exit'); | |
| #endif | |
| #if !EXIT_RUNTIME && ASSERTIONS | |
| warnOnce('emscripten_force_exit cannot actually shut down the runtime, as the build does not have EXIT_RUNTIME set'); | |
| #endif | |
| __emscripten_runtime_keepalive_clear(); | |
| _exit(status); | |
| }, | |
| emscripten_out: (str) => out(UTF8ToString(str)), | |
| emscripten_outn: (str, len) => out(UTF8ToString(str, len)), | |
| emscripten_err: (str) => err(UTF8ToString(str)), | |
| emscripten_errn: (str, len) => err(UTF8ToString(str, len)), | |
| #if ASSERTIONS || RUNTIME_DEBUG | |
| emscripten_dbg: (str) => dbg(UTF8ToString(str)), | |
| emscripten_dbgn: (str, len) => dbg(UTF8ToString(str, len)), | |
| emscripten_dbg_backtrace: (str) => { | |
| dbg(UTF8ToString(str) + '\n' + new Error().stack); | |
| }, | |
| #endif | |
| // Use program_invocation_short_name and program_invocation_name in compiled | |
| // programs. This function is for implementing them. | |
| _emscripten_get_progname__deps: ['$getExecutableName', '$stringToUTF8'], | |
| _emscripten_get_progname: (str, len) => stringToUTF8(getExecutableName(), str, len), | |
| emscripten_console_log: (str) => { | |
| #if ASSERTIONS | |
| assert(typeof str == 'number'); | |
| #endif | |
| console.log(UTF8ToString(str)); | |
| }, | |
| emscripten_console_warn: (str) => { | |
| #if ASSERTIONS | |
| assert(typeof str == 'number'); | |
| #endif | |
| console.warn(UTF8ToString(str)); | |
| }, | |
| emscripten_console_error: (str) => { | |
| #if ASSERTIONS | |
| assert(typeof str == 'number'); | |
| #endif | |
| console.error(UTF8ToString(str)); | |
| }, | |
| emscripten_console_trace: (str) => { | |
| #if ASSERTIONS | |
| assert(typeof str == 'number'); | |
| #endif | |
| console.trace(UTF8ToString(str)); | |
| }, | |
| emscripten_throw_number: (number) => { | |
| throw number; | |
| }, | |
| emscripten_throw_string: (str) => { | |
| #if ASSERTIONS | |
| assert(typeof str == 'number'); | |
| #endif | |
| throw UTF8ToString(str); | |
| }, | |
| #if !MINIMAL_RUNTIME | |
| #if STACK_OVERFLOW_CHECK | |
| $handleException__deps: ['emscripten_stack_get_current'], | |
| #endif | |
| $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') { | |
| #if RUNTIME_DEBUG | |
| dbg(`handleException: unwinding: EXITSTATUS=${EXITSTATUS}`); | |
| #endif | |
| return EXITSTATUS; | |
| } | |
| #if STACK_OVERFLOW_CHECK | |
| checkStackCookie(); | |
| if (e instanceof WebAssembly.RuntimeError) { | |
| if (_emscripten_stack_get_current() <= 0) { | |
| err('Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to {{{ STACK_SIZE }}})'); | |
| } | |
| } | |
| #endif | |
| #if RUNTIME_DEBUG | |
| dbg("handleException: got unexpected exception, calling quit_") | |
| #endif | |
| quit_(1, e); | |
| }, | |
| $runtimeKeepaliveCounter__internal: true, | |
| $runtimeKeepaliveCounter: 0, | |
| #if isSymbolNeeded('$noExitRuntime') | |
| // If the `noExitRuntime` symbol is included in the build then | |
| // keepRuntimeAlive is always conditional since its state can change | |
| // at runtime. | |
| $keepRuntimeAlive__deps: ['$runtimeKeepaliveCounter'], | |
| $keepRuntimeAlive: () => noExitRuntime || runtimeKeepaliveCounter > 0, | |
| #elif !EXIT_RUNTIME && !PTHREADS | |
| // When `noExitRuntime` is not included and EXIT_RUNTIME=0 then we know the | |
| // runtime can never exit (i.e. should always be kept alive). | |
| // However, since pthreads themselves always need to be able to exit we | |
| // have to track `runtimeKeepaliveCounter` in that case. | |
| $keepRuntimeAlive: () => true, | |
| #else | |
| $keepRuntimeAlive__deps: ['$runtimeKeepaliveCounter'], | |
| $keepRuntimeAlive: () => runtimeKeepaliveCounter > 0, | |
| #endif | |
| // Callable in pthread without __proxy needed. | |
| $runtimeKeepalivePush__deps: ['$runtimeKeepaliveCounter'], | |
| $runtimeKeepalivePush__sig: 'v', | |
| $runtimeKeepalivePush: () => { | |
| runtimeKeepaliveCounter += 1; | |
| #if RUNTIME_DEBUG | |
| dbg(`runtimeKeepalivePush -> counter=${runtimeKeepaliveCounter}`); | |
| #endif | |
| }, | |
| $runtimeKeepalivePop__deps: ['$runtimeKeepaliveCounter'], | |
| $runtimeKeepalivePop__sig: 'v', | |
| $runtimeKeepalivePop: () => { | |
| #if ASSERTIONS | |
| assert(runtimeKeepaliveCounter > 0); | |
| #endif | |
| runtimeKeepaliveCounter -= 1; | |
| #if RUNTIME_DEBUG | |
| dbg(`runtimeKeepalivePop -> counter=${runtimeKeepaliveCounter}`); | |
| #endif | |
| }, | |
| emscripten_runtime_keepalive_push: '$runtimeKeepalivePush', | |
| emscripten_runtime_keepalive_pop: '$runtimeKeepalivePop', | |
| emscripten_runtime_keepalive_check: '$keepRuntimeAlive', | |
| // Used to call user callbacks from the embedder / event loop. For example | |
| // setTimeout or any other kind of event handler that calls into user case | |
| // needs to use this wrapper. | |
| // | |
| // The job of this wrapper is the handle emscripten-specific exceptions such | |
| // as ExitStatus and 'unwind' and prevent these from escaping to the top | |
| // level. | |
| $callUserCallback__deps: ['$handleException', '$maybeExit'], | |
| $callUserCallback: (func) => { | |
| #if EXIT_RUNTIME | |
| if (runtimeExited || ABORT) { | |
| #else | |
| if (ABORT) { | |
| #endif | |
| #if ASSERTIONS | |
| err('user callback triggered after runtime exited or application aborted. Ignoring.'); | |
| #endif | |
| return; | |
| } | |
| try { | |
| return func(); | |
| } catch (e) { | |
| handleException(e); | |
| } finally { | |
| maybeExit(); | |
| } | |
| }, | |
| $maybeExit__deps: ['exit', '$handleException', '$keepRuntimeAlive', | |
| #if PTHREADS | |
| '_emscripten_thread_exit', | |
| #endif | |
| #if RUNTIME_DEBUG >= 2 | |
| '$runtimeKeepaliveCounter', | |
| #endif | |
| ], | |
| $maybeExit: () => { | |
| #if EXIT_RUNTIME | |
| if (runtimeExited) { | |
| return; | |
| } | |
| #endif | |
| #if RUNTIME_DEBUG >= 2 | |
| dbg(`maybeExit: user callback done: runtimeKeepaliveCounter=${runtimeKeepaliveCounter}`); | |
| #endif | |
| if (!keepRuntimeAlive()) { | |
| #if RUNTIME_DEBUG | |
| dbg(`maybeExit: calling exit() implicitly after user callback completed: ${EXITSTATUS}`); | |
| #endif | |
| try { | |
| #if PTHREADS | |
| if (ENVIRONMENT_IS_PTHREAD) { | |
| // exit the current thread, but only if there is one active. | |
| // TODO(https://github.com/emscripten-core/emscripten/issues/25076): | |
| // Unify this check with the runtimeExited check above | |
| if (_pthread_self()) __emscripten_thread_exit(EXITSTATUS); | |
| return; | |
| } | |
| #endif | |
| _exit(EXITSTATUS); | |
| } catch (e) { | |
| handleException(e); | |
| } | |
| } | |
| }, | |
| $asyncLoad: async (url) => { | |
| var arrayBuffer = await readAsync(url); | |
| #if ASSERTIONS | |
| assert(arrayBuffer, `Loading data file "${url}" failed (no arrayBuffer).`); | |
| #endif | |
| return new Uint8Array(arrayBuffer); | |
| }, | |
| #else // MINIMAL_RUNTIME | |
| $callUserCallback: (func) => { | |
| // MINIMAL_RUNTIME doesn't support the runtimeKeepalive stuff, but under | |
| // some circumstances it supportes `runtimeExited` | |
| #if EXIT_RUNTIME | |
| if (runtimeExited) { | |
| #if ASSERTIONS | |
| err('user callback triggered after runtime exited or application aborted. Ignoring.'); | |
| #endif | |
| return; | |
| } | |
| #endif | |
| func(); | |
| }, | |
| #endif // MINIMAL_RUNTIME | |
| $asmjsMangle: (x) => { | |
| if (x == '__main_argc_argv') { | |
| x = 'main'; | |
| } | |
| #if DYNCALLS | |
| return x.startsWith('dynCall_') ? x : '_' + x; | |
| #else | |
| return '_' + x; | |
| #endif | |
| }, | |
| $alignMemory: (size, alignment) => { | |
| #if ASSERTIONS | |
| assert(alignment, "alignment argument is required"); | |
| #endif | |
| return Math.ceil(size / alignment) * alignment; | |
| }, | |
| // Allocate memory for an mmap operation. This allocates space of the right | |
| // page-aligned size, and clears the allocated space. | |
| #if hasExportedSymbol('emscripten_builtin_memalign') | |
| $mmapAlloc__deps: ['$zeroMemory', '$alignMemory'], | |
| #endif | |
| $mmapAlloc: (size) => { | |
| #if hasExportedSymbol('emscripten_builtin_memalign') | |
| size = alignMemory(size, {{{ WASM_PAGE_SIZE }}}); | |
| var ptr = _emscripten_builtin_memalign({{{ WASM_PAGE_SIZE }}}, size); | |
| if (ptr) zeroMemory(ptr, size); | |
| return ptr; | |
| #elif ASSERTIONS | |
| abort('internal error: mmapAlloc called but `emscripten_builtin_memalign` native symbol not exported'); | |
| #else | |
| abort(); | |
| #endif | |
| }, | |
| _emscripten_fs_load_embedded_files__deps: ['$FS', '$PATH'], | |
| _emscripten_fs_load_embedded_files: (ptr) => { | |
| #if RUNTIME_DEBUG | |
| dbg('preloading data files'); | |
| #endif | |
| do { | |
| var name_addr = {{{ makeGetValue('ptr', '0', '*') }}}; | |
| ptr += {{{ POINTER_SIZE }}}; | |
| var len = {{{ makeGetValue('ptr', '0', '*') }}}; | |
| ptr += {{{ POINTER_SIZE }}}; | |
| var content = {{{ makeGetValue('ptr', '0', '*') }}}; | |
| ptr += {{{ POINTER_SIZE }}}; | |
| var name = UTF8ToString(name_addr) | |
| #if RUNTIME_DEBUG | |
| dbg(`preloading files: ${name}`); | |
| #endif | |
| FS.createPath('/', PATH.dirname(name), true, true); | |
| // canOwn this data in the filesystem, it is a slice of wasm memory that will never change | |
| FS.createDataFile(name, null, HEAP8.subarray(content, content + len), true, true, /*canOwn=*/true); | |
| } while ({{{ makeGetValue('ptr', '0', '*') }}}); | |
| #if RUNTIME_DEBUG | |
| dbg('done preloading data files'); | |
| #endif | |
| }, | |
| $HandleAllocator: class { | |
| allocated = [undefined]; | |
| freelist = []; | |
| get(id) { | |
| #if ASSERTIONS | |
| assert(this.allocated[id] !== undefined, `invalid handle: ${id}`); | |
| #endif | |
| return this.allocated[id]; | |
| } | |
| has(id) { | |
| return this.allocated[id] !== undefined; | |
| } | |
| allocate(handle) { | |
| var id = this.freelist.pop() || this.allocated.length; | |
| this.allocated[id] = handle; | |
| return id; | |
| } | |
| free(id) { | |
| #if ASSERTIONS | |
| assert(this.allocated[id] !== undefined); | |
| #endif | |
| // Set the slot to `undefined` rather than using `delete` here since | |
| // apparently arrays with holes in them can be less efficient. | |
| this.allocated[id] = undefined; | |
| this.freelist.push(id); | |
| } | |
| }, | |
| // `wasmTable` is a JS alias for the Wasm `__indirect_function_table` export | |
| $wasmTable__docs: '/** @type {WebAssembly.Table} */', | |
| $wasmTable: '__indirect_function_table', | |
| #if IMPORTED_MEMORY | |
| // This gets defined in src/runtime_init_memory.js | |
| $wasmMemory: undefined, | |
| #else | |
| // `wasmMemory` is a JS alias for the Wasm `memory` export | |
| $wasmMemory: 'memory', | |
| #endif | |
| $getUniqueRunDependency: (id) => { | |
| #if ASSERTIONS | |
| var orig = id; | |
| while (1) { | |
| if (!runDependencyTracking[id]) return id; | |
| id = orig + Math.random(); | |
| } | |
| #else | |
| return id; | |
| #endif | |
| }, | |
| $noExitRuntime__postset: () => addAtModule(makeModuleReceive('noExitRuntime')), | |
| $noExitRuntime: {{{ !EXIT_RUNTIME }}}, | |
| #if !MINIMAL_RUNTIME | |
| // 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. | |
| $runDependencies__internal: true, | |
| $runDependencies: 0, | |
| // overridden to take different actions when all run dependencies are fulfilled | |
| $dependenciesFulfilled__internal: true, | |
| $dependenciesFulfilled: null, | |
| #if ASSERTIONS | |
| $runDependencyTracking__internal: true, | |
| $runDependencyTracking: {}, | |
| $runDependencyWatcher__internal: true, | |
| $runDependencyWatcher: null, | |
| #endif | |
| $addRunDependency__deps: ['$runDependencies', '$removeRunDependency', | |
| #if ASSERTIONS | |
| '$runDependencyTracking', | |
| '$runDependencyWatcher', | |
| #endif | |
| ], | |
| $addRunDependency: (id) => { | |
| runDependencies++; | |
| #if expectToReceiveOnModule('monitorRunDependencies') | |
| Module['monitorRunDependencies']?.(runDependencies); | |
| #endif | |
| #if ASSERTIONS | |
| #if RUNTIME_DEBUG | |
| dbg('addRunDependency', id); | |
| #endif | |
| assert(id, 'addRunDependency requires an ID') | |
| assert(!runDependencyTracking[id]); | |
| runDependencyTracking[id] = 1; | |
| if (runDependencyWatcher === null && globalThis.setInterval) { | |
| // Check for missing dependencies every few seconds | |
| runDependencyWatcher = setInterval(() => { | |
| if (ABORT) { | |
| clearInterval(runDependencyWatcher); | |
| runDependencyWatcher = null; | |
| return; | |
| } | |
| var shown = false; | |
| for (var dep in runDependencyTracking) { | |
| if (!shown) { | |
| shown = true; | |
| err('still waiting on run dependencies:'); | |
| } | |
| err(`dependency: ${dep}`); | |
| } | |
| if (shown) { | |
| err('(end of list)'); | |
| } | |
| }, 10000); | |
| #if ENVIRONMENT_MAY_BE_NODE | |
| // Prevent this timer from keeping the runtime alive if nothing | |
| // else is. | |
| runDependencyWatcher.unref?.() | |
| #endif | |
| } | |
| #endif | |
| }, | |
| $removeRunDependency__deps: ['$runDependencies', '$dependenciesFulfilled', | |
| #if ASSERTIONS | |
| '$runDependencyTracking', | |
| '$runDependencyWatcher', | |
| #endif | |
| ], | |
| $removeRunDependency: (id) => { | |
| runDependencies--; | |
| #if expectToReceiveOnModule('monitorRunDependencies') | |
| Module['monitorRunDependencies']?.(runDependencies); | |
| #endif | |
| #if ASSERTIONS | |
| #if RUNTIME_DEBUG | |
| dbg('removeRunDependency', id); | |
| #endif | |
| assert(id, 'removeRunDependency requires an ID'); | |
| assert(runDependencyTracking[id]); | |
| delete runDependencyTracking[id]; | |
| #endif | |
| if (runDependencies == 0) { | |
| #if ASSERTIONS | |
| if (runDependencyWatcher !== null) { | |
| clearInterval(runDependencyWatcher); | |
| runDependencyWatcher = null; | |
| } | |
| #endif | |
| if (dependenciesFulfilled) { | |
| var callback = dependenciesFulfilled; | |
| dependenciesFulfilled = null; | |
| callback(); // can add another dependenciesFulfilled | |
| } | |
| } | |
| }, | |
| #endif | |
| // The following addOn<X> functions are for adding runtime callbacks at | |
| // various executions points. Each addOn<X> function has a corresponding | |
| // compiled time version named addAt<X> that will instead inline during | |
| // compilation (see parseTools.mjs). | |
| // Note: if there are both runtime and compile time code, the runtime | |
| // callbacks will be invoked before the compile time code. | |
| // See ATPRERUNS in parseTools.mjs for more information. | |
| $onPreRuns: [], | |
| $onPreRuns__internal: true, | |
| $onPreRuns__deps: ['$callRuntimeCallbacks'], | |
| $onPreRuns__postset: () => { | |
| ATPRERUNS.unshift('callRuntimeCallbacks(onPreRuns);'); | |
| }, | |
| $addOnPreRun__deps: ['$onPreRuns'], | |
| $addOnPreRun: (cb) => onPreRuns.push(cb), | |
| // See ATINITS in parseTools.mjs for more information. | |
| $onInits: [], | |
| $onInits__internal: true, | |
| $onInits__deps: ['$callRuntimeCallbacks'], | |
| $onInits__postset: () => { | |
| ATINITS.unshift('callRuntimeCallbacks(onInits);'); | |
| }, | |
| $addOnInit__deps: ['$onInits'], | |
| $addOnInit: (cb) => onInits.push(cb), | |
| // See ATPOSTCTORS in parseTools.mjs for more information. | |
| $onPostCtors: [], | |
| $onPostCtors__internal: true, | |
| $onPostCtors__deps: ['$callRuntimeCallbacks'], | |
| $onPostCtors__postset: () => { | |
| ATPOSTCTORS.unshift('callRuntimeCallbacks(onPostCtors);'); | |
| }, | |
| $addOnPostCtor__deps: ['$onPostCtors'], | |
| $addOnPostCtor: (cb) => onPostCtors.push(cb), | |
| // See ATMAINS in parseTools.mjs for more information. | |
| $onMains: [], | |
| $onMains__internal: true, | |
| $onMains__deps: ['$callRuntimeCallbacks'], | |
| $onMains__postset: () => { | |
| ATMAINS.unshift('callRuntimeCallbacks(onMains);'); | |
| }, | |
| $addOnPreMain__deps: ['$onMains'], | |
| $addOnPreMain: (cb) => onMains.push(cb), | |
| // See ATEXITS in parseTools.mjs for more information. | |
| $onExits: [], | |
| $onExits__internal: true, | |
| $onExits__deps: ['$callRuntimeCallbacks'], | |
| $onExits__postset: () => { | |
| ATEXITS.unshift('callRuntimeCallbacks(onExits);'); | |
| }, | |
| $addOnExit__deps: ['$onExits'], | |
| $addOnExit: (cb) => onExits.push(cb), | |
| // See ATPOSTRUNS in parseTools.mjs for more information. | |
| $onPostRuns: [], | |
| $onPostRuns__internal: true, | |
| $onPostRuns__deps: ['$callRuntimeCallbacks'], | |
| $onPostRuns__postset: () => { | |
| ATPOSTRUNS.unshift('callRuntimeCallbacks(onPostRuns);'); | |
| }, | |
| $addOnPostRun__deps: ['$onPostRuns'], | |
| $addOnPostRun: (cb) => onPostRuns.push(cb), | |
| // We used to define these globals unconditionally in support code. | |
| // Instead, we now define them here so folks can pull it in explicitly, on | |
| // demand. | |
| $STACK_SIZE: {{{ STACK_SIZE }}}, | |
| $STACK_ALIGN: {{{ STACK_ALIGN }}}, | |
| $POINTER_SIZE: {{{ POINTER_SIZE }}}, | |
| $ASSERTIONS: {{{ ASSERTIONS }}}, | |
| }); | |
| function autoAddDeps(lib, name) { | |
| for (const item of Object.keys(lib)) { | |
| if (!isDecorator(item)) { | |
| lib[item + '__deps'] ??= []; | |
| lib[item + '__deps'].push(name); | |
| } | |
| } | |
| } | |
| #if LEGACY_RUNTIME | |
| // Library functions that were previously included as runtime functions are | |
| // automatically included when `LEGACY_RUNTIME` is set. | |
| extraLibraryFuncs.push( | |
| '$addFunction', | |
| '$removeFunction', | |
| '$allocate', | |
| '$ALLOC_NORMAL', | |
| '$ALLOC_STACK', | |
| '$AsciiToString', | |
| '$stringToAscii', | |
| '$UTF16ToString', | |
| '$stringToUTF16', | |
| '$lengthBytesUTF16', | |
| '$UTF32ToString', | |
| '$stringToUTF32', | |
| '$lengthBytesUTF32', | |
| '$stringToNewUTF8', | |
| '$stringToUTF8OnStack', | |
| '$writeStringToMemory', | |
| '$writeArrayToMemory', | |
| '$writeAsciiToMemory', | |
| '$intArrayFromString', | |
| '$intArrayToString', | |
| '$warnOnce', | |
| '$ccall', | |
| '$cwrap', | |
| '$ExitStatus', | |
| '$UTF8ArrayToString', | |
| '$UTF8ToString', | |
| '$stringToUTF8Array', | |
| '$stringToUTF8', | |
| '$lengthBytesUTF8', | |
| ); | |
| #endif | |
| function wrapSyscallFunction(x, library, isWasi) { | |
| if (isJsOnlySymbol(x) || isDecorator(x)) { | |
| return; | |
| } | |
| var t = library[x]; | |
| if (typeof t == 'string') return; | |
| t = t.toString(); | |
| // If a syscall uses FS, but !SYSCALLS_REQUIRE_FILESYSTEM, then the user | |
| // has disabled the filesystem or we have proven some other way that this will | |
| // not be called in practice, and do not need that code. | |
| if (!SYSCALLS_REQUIRE_FILESYSTEM && t.includes('FS.')) { | |
| library[x + '__deps'] = []; | |
| t = modifyJSFunction(t, (args, body) => { | |
| return `(${args}) => {\n` + | |
| (ASSERTIONS ? "abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM');\n" : '') + | |
| '}'; | |
| }); | |
| } | |
| var isVariadic = !isWasi && t.includes(', varargs'); | |
| #if SYSCALLS_REQUIRE_FILESYSTEM | |
| var canThrow = library[x + '__nothrow'] !== true; | |
| #else | |
| var canThrow = false; | |
| #endif | |
| library[x + '__deps'] ??= []; | |
| #if PURE_WASI && !GROWABLE_ARRAYBUFFERS | |
| // In PURE_WASI mode we can't assume the wasm binary was built by emscripten | |
| // and politely notify us on memory growth. Instead we have to check for | |
| // possible memory growth on each syscall. | |
| var pre = '\nif (!HEAPU8.byteLength) _emscripten_notify_memory_growth(0);\n' | |
| library[x + '__deps'].push('emscripten_notify_memory_growth'); | |
| #else | |
| var pre = ''; | |
| #endif | |
| var post = ''; | |
| if (isVariadic) { | |
| pre += 'SYSCALLS.varargs = varargs;\n'; | |
| } | |
| #if SYSCALL_DEBUG | |
| if (isVariadic) { | |
| if (canThrow) { | |
| post += 'finally { SYSCALLS.varargs = undefined; }\n'; | |
| } else { | |
| post += 'SYSCALLS.varargs = undefined;\n'; | |
| } | |
| } | |
| pre += `dbg('syscall! ${x}: [' + Array.prototype.slice.call(arguments) + ']');\n`; | |
| pre += "var canWarn = true;\n"; | |
| pre += "var ret = (() => {"; | |
| post += "})();\n"; | |
| post += "if (ret && ret < 0 && canWarn) {\n"; | |
| post += " dbg(`error: syscall may have failed with ${-ret} (${strError(-ret)})`);\n"; | |
| post += "}\n"; | |
| post += "dbg(`syscall return: ${ret}`);\n"; | |
| post += "return ret;\n"; | |
| // Emit dependency to strError() since we added use of it above. | |
| library[x + '__deps'].push('$strError'); | |
| #endif | |
| delete library[x + '__nothrow']; | |
| var handler = ''; | |
| if (canThrow) { | |
| pre += 'try {\n'; | |
| handler += | |
| "} catch (e) {\n" + | |
| " if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e;\n"; | |
| #if SYSCALL_DEBUG | |
| handler += | |
| " dbg(`error: syscall failed with ${e.errno} (${strError(e.errno)})`);\n" + | |
| " canWarn = false;\n"; | |
| #endif | |
| // Musl syscalls are negated. | |
| if (isWasi) { | |
| handler += " return e.errno;\n"; | |
| } else { | |
| // Musl syscalls are negated. | |
| handler += " return -e.errno;\n"; | |
| } | |
| handler += "}\n"; | |
| } | |
| post = handler + post; | |
| if (pre || post) { | |
| t = modifyJSFunction(t, (args, body, async_) => `${async_}function (${args}) {\n${pre}${body}${post}}\n`); | |
| } | |
| library[x] = t; | |
| // Automatically add dependency on `$SYSCALLS` | |
| if (!WASMFS && t.includes('SYSCALLS')) { | |
| library[x + '__deps'].push('$SYSCALLS'); | |
| } | |
| #if PTHREADS | |
| // Most syscalls need to happen on the main JS thread (e.g. because the | |
| // filesystem is in JS and on that thread). Proxy synchronously to there. | |
| // There are some exceptions, syscalls that we know are ok to just run in | |
| // any thread; those are marked as not being proxied with | |
| // __proxy: false | |
| // A syscall without a return value could perhaps be proxied asynchronously | |
| // instead of synchronously, and marked with | |
| // __proxy: 'async' | |
| // (but essentially all syscalls do have return values). | |
| library[x + '__proxy'] ??= 'sync'; | |
| #endif | |
| } | |
Xet Storage Details
- Size:
- 91.2 kB
- Xet hash:
- 9768d39bac032961d2e32bc2c465936efb303247079d3d9c24cb77693d35288d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.