| {{{ |
| global.captureModuleArg = () => MODULARIZE ? '' : 'self.Module=d;'; |
| global.instantiateModule = () => MODULARIZE ? `${EXPORT_NAME}(d);` : ''; |
| global.instantiateWasm = () => MINIMAL_RUNTIME ? '' : 'd[`instantiateWasm`]=(i,r)=>{var n=new WebAssembly.Instance(d[`wasm`],i);r(n,d[`wasm`]);return n.exports};'; |
| null; |
| }}} |
| |
| #if WASM_WORKERS |
| |
| #if !SHARED_MEMORY |
| #error "Internal error! SHARED_MEMORY should be enabled when building with WASM_WORKERS" |
| #endif |
| #if SINGLE_FILE |
| #error "-sSINGLE_FILE is not supported with -sWASM_WORKERS!" |
| #endif |
| #if LINKABLE |
| #error "-sLINKABLE is not supported with -sWASM_WORKERS!" |
| #endif |
| #if SIDE_MODULE |
| #error "-sSIDE_MODULE is not supported with -sWASM_WORKERS!" |
| #endif |
| #if MAIN_MODULE |
| #error "-sMAIN_MODULE is not supported with -sWASM_WORKERS!" |
| #endif |
| #if PROXY_TO_WORKER |
| #error "-sPROXY_TO_WORKER is not supported with -sWASM_WORKERS!" |
| #endif |
| |
| #endif // ~WASM_WORKERS |
| |
| |
| addToLibrary({ |
| $_wasmWorkers: {}, |
| $_wasmWorkersID: 1, |
| |
| // Starting up a Wasm Worker is an asynchronous operation, hence if the parent |
| // thread performs any postMessage()-based wasm function calls s to the |
| // Worker, they must be delayed until the async startup has finished, after |
| // which these postponed function calls can be dispatched. |
| $_wasmWorkerDelayedMessageQueue: [], |
| |
| $_wasmWorkerAppendToQueue: (e) => { |
| _wasmWorkerDelayedMessageQueue.push(e); |
| }, |
| |
| // Executes a wasm function call received via a postMessage. |
| $_wasmWorkerRunPostMessage: (e) => { |
| // '_wsc' is short for 'wasm call', trying to use an identifier name that |
| // will never conflict with user code |
| let data = e.data, wasmCall = data['_wsc']; |
| wasmCall && getWasmTableEntry(wasmCall)(...data['x']); |
| }, |
| |
| // src/postamble_minimal.js brings this symbol in to the build, and calls this |
| // function synchronously from main JS file at the startup of each Worker. |
| $_wasmWorkerInitializeRuntime__deps: ['$_wasmWorkerDelayedMessageQueue', '$_wasmWorkerRunPostMessage', '$_wasmWorkerAppendToQueue', 'emscripten_wasm_worker_initialize'], |
| $_wasmWorkerInitializeRuntime: () => { |
| let m = Module; |
| #if ASSERTIONS |
| assert(m['sb'] % 16 == 0); |
| assert(m['sz'] % 16 == 0); |
| #endif |
| |
| #if STACK_OVERFLOW_CHECK >= 2 |
| // _emscripten_wasm_worker_initialize() initializes the stack for this |
| // Worker, but it cannot call to extern __set_stack_limits() function, or |
| // Binaryen breaks with "Fatal: Module::addFunction: __set_stack_limits |
| // already exists". So for now, invoke this function from JS side. TODO: |
| // remove this in the future. Note that this call is not exactly correct, |
| // since this limit will include the TLS slot, that will be part of the |
| // region between m['sb'] and m['sz'], so we need to fix up the call below. |
| ___set_stack_limits(m['sb'] + m['sz'], m['sb']); |
| #endif |
| // Run the C side Worker initialization for stack and TLS. |
| _emscripten_wasm_worker_initialize(m['sb'], m['sz']); |
| #if PTHREADS |
| // Record that this Wasm Worker supports synchronous blocking in emscripten_futex_wake(). |
| ___set_thread_state(/*thread_ptr=*/0, /*is_main_thread=*/0, /*is_runtime_thread=*/0, /*supports_wait=*/0); |
| #endif |
| #if STACK_OVERFLOW_CHECK >= 2 |
| // Fix up stack base. (TLS frame is created at the bottom address end of the stack) |
| // See https://github.com/emscripten-core/emscripten/issues/16496 |
| ___set_stack_limits(_emscripten_stack_get_base(), _emscripten_stack_get_end()); |
| #endif |
| |
| #if AUDIO_WORKLET |
| // Audio Worklets do not have postMessage()ing capabilities. |
| if (typeof AudioWorkletGlobalScope === 'undefined') { |
| #endif |
| // The Wasm Worker runtime is now up, so we can start processing |
| // any postMessage function calls that have been received. Drop the temp |
| // message handler that queued any pending incoming postMessage function calls ... |
| removeEventListener('message', _wasmWorkerAppendToQueue); |
| // ... then flush whatever messages we may have already gotten in the queue, |
| // and clear _wasmWorkerDelayedMessageQueue to undefined ... |
| _wasmWorkerDelayedMessageQueue = _wasmWorkerDelayedMessageQueue.forEach(_wasmWorkerRunPostMessage); |
| // ... and finally register the proper postMessage handler that immediately |
| // dispatches incoming function calls without queueing them. |
| addEventListener('message', _wasmWorkerRunPostMessage); |
| #if AUDIO_WORKLET |
| } |
| #endif |
| }, |
| |
| #if WASM_WORKERS == 2 |
| // In WASM_WORKERS == 2 build mode, we create the Wasm Worker global scope |
| // script from a string bundled in the main application JS file. This |
| // simplifies the number of deployed JS files with the app, but has a downside |
| // that the generated build output will no longer be csp-eval compliant. |
| // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#unsafe_eval_expressions |
| $_wasmWorkerBlobUrl: "URL.createObjectURL(new Blob(['onmessage=function(d){onmessage=null;d=d.data;{{{ captureModuleArg() }}}{{{ instantiateWasm() }}}importScripts(d.js);{{{ instantiateModule() }}}d.wasm=d.mem=d.js=0;}'],{type:'application/javascript'}))", |
| #endif |
| |
| _emscripten_create_wasm_worker__deps: [ |
| '$_wasmWorkers', '$_wasmWorkersID', |
| '$_wasmWorkerAppendToQueue', '$_wasmWorkerRunPostMessage', |
| #if WASM_WORKERS == 2 |
| '$_wasmWorkerBlobUrl', |
| #endif |
| ], |
| _emscripten_create_wasm_worker__postset: ` |
| if (ENVIRONMENT_IS_WASM_WORKER) { |
| _wasmWorkers[0] = this; |
| addEventListener("message", _wasmWorkerAppendToQueue); |
| }`, |
| _emscripten_create_wasm_worker: (stackLowestAddress, stackSize) => { |
| let worker = _wasmWorkers[_wasmWorkersID] = new Worker( |
| #if WASM_WORKERS == 2 |
| // WASM_WORKERS=2 mode embeds .ww.js file contents into the main .js file |
| // as a Blob URL. (convenient, but not CSP security safe, since this is |
| // eval-like) |
| _wasmWorkerBlobUrl |
| #elif MINIMAL_RUNTIME |
| // MINIMAL_RUNTIME has a structure where the .ww.js file is loaded from |
| // the main HTML file in parallel to all other files for best performance |
| Module['$wb'] // $wb="Wasm worker Blob", abbreviated since not DCEable |
| #else |
| // default runtime loads the .ww.js file on demand. |
| locateFile('{{{ WASM_WORKER_FILE }}}') |
| #endif |
| ); |
| // Craft the Module object for the Wasm Worker scope: |
| worker.postMessage({ |
| // Signal with a non-zero value that this Worker will be a Wasm Worker, |
| // and not the main browser thread. |
| '$ww': _wasmWorkersID, |
| #if MINIMAL_RUNTIME |
| 'wasm': Module['wasm'], |
| 'js': Module['js'], |
| 'mem': wasmMemory, |
| #else |
| 'wasm': wasmModule, |
| 'js': Module['mainScriptUrlOrBlob'] || _scriptDir, |
| 'wasmMemory': wasmMemory, |
| #endif |
| 'sb': stackLowestAddress, // sb = stack bottom (lowest stack address, SP points at this when stack is full) |
| 'sz': stackSize, // sz = stack size |
| }); |
| worker.onmessage = _wasmWorkerRunPostMessage; |
| return _wasmWorkersID++; |
| }, |
| |
| emscripten_terminate_wasm_worker: (id) => { |
| #if ASSERTIONS |
| assert(id != 0, 'emscripten_terminate_wasm_worker() cannot be called with id=0!'); |
| #endif |
| if (_wasmWorkers[id]) { |
| _wasmWorkers[id].terminate(); |
| delete _wasmWorkers[id]; |
| } |
| }, |
| |
| emscripten_terminate_all_wasm_workers: () => { |
| #if ASSERTIONS |
| assert(!ENVIRONMENT_IS_WASM_WORKER, 'emscripten_terminate_all_wasm_workers() cannot be called from a Wasm Worker: only the main browser thread has visibility to terminate all Workers!'); |
| #endif |
| Object.values(_wasmWorkers).forEach((worker) => { |
| worker.terminate(); |
| }); |
| _wasmWorkers = {}; |
| }, |
| |
| emscripten_current_thread_is_wasm_worker: () => { |
| #if WASM_WORKERS |
| return ENVIRONMENT_IS_WASM_WORKER; |
| #else |
| // implicit return 0; |
| #endif |
| }, |
| |
| emscripten_wasm_worker_self_id: () => { |
| return Module['$ww']; |
| }, |
| |
| emscripten_wasm_worker_post_function_v: (id, funcPtr) => { |
| _wasmWorkers[id].postMessage({'_wsc': funcPtr, 'x': [] }); // "WaSm Call" |
| }, |
| |
| $_wasmWorkerPostFunction1__sig: 'vipd', |
| $_wasmWorkerPostFunction1: (id, funcPtr, arg0) => { |
| _wasmWorkers[id].postMessage({'_wsc': funcPtr, 'x': [arg0] }); // "WaSm Call" |
| }, |
| |
| emscripten_wasm_worker_post_function_vi: '$_wasmWorkerPostFunction1', |
| emscripten_wasm_worker_post_function_vd: '$_wasmWorkerPostFunction1', |
| |
| $_wasmWorkerPostFunction2__sig: 'vipdd', |
| $_wasmWorkerPostFunction2: (id, funcPtr, arg0, arg1) => { |
| _wasmWorkers[id].postMessage({'_wsc': funcPtr, 'x': [arg0, arg1] }); // "WaSm Call" |
| }, |
| emscripten_wasm_worker_post_function_vii: '$_wasmWorkerPostFunction2', |
| emscripten_wasm_worker_post_function_vdd: '$_wasmWorkerPostFunction2', |
| |
| $_wasmWorkerPostFunction3__sig: 'vipddd', |
| $_wasmWorkerPostFunction3: (id, funcPtr, arg0, arg1, arg2) => { |
| _wasmWorkers[id].postMessage({'_wsc': funcPtr, 'x': [arg0, arg1, arg2] }); // "WaSm Call" |
| }, |
| emscripten_wasm_worker_post_function_viii: '$_wasmWorkerPostFunction3', |
| emscripten_wasm_worker_post_function_vddd: '$_wasmWorkerPostFunction3', |
| |
| emscripten_wasm_worker_post_function_sig__deps: ['$readEmAsmArgs'], |
| emscripten_wasm_worker_post_function_sig: (id, funcPtr, sigPtr, varargs) => { |
| #if ASSERTIONS |
| assert(id >= 0); |
| assert(funcPtr); |
| assert(sigPtr); |
| assert(UTF8ToString(sigPtr)[0] != 'v', 'Do NOT specify the return argument in the signature string for a call to emscripten_wasm_worker_post_function_sig(), just pass the function arguments.'); |
| assert(varargs); |
| #endif |
| _wasmWorkers[id].postMessage({'_wsc': funcPtr, 'x': readEmAsmArgs(sigPtr, varargs) }); |
| }, |
| |
| $atomicWaitStates: "['ok', 'not-equal', 'timed-out']", |
| |
| // Chrome 87 (and hence Edge 87) shipped Atomics.waitAsync (https://www.chromestatus.com/feature/6243382101803008) |
| // However its implementation is faulty: https://bugs.chromium.org/p/chromium/issues/detail?id=1167541 |
| // Firefox Nightly 86.0a1 (2021-01-15) does not yet have it, https://bugzilla.mozilla.org/show_bug.cgi?id=1467846 |
| // And at the time of writing, no other browser has it either. |
| #if MIN_EDGE_VERSION < 91 || MIN_CHROME_VERSION < 91 || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED || MIN_FIREFOX_VERSION != TARGET_NOT_SUPPORTED || ENVIRONMENT_MAY_BE_NODE |
| // Partially polyfill Atomics.waitAsync() if not available in the browser. |
| // Also polyfill for old Chrome-based browsers, where Atomics.waitAsync is |
| // broken until Chrome 91, see |
| // https://bugs.chromium.org/p/chromium/issues/detail?id=1167541 |
| // https://github.com/tc39/proposal-atomics-wait-async/blob/master/PROPOSAL.md |
| // This polyfill performs polling with setTimeout() to observe a change in the |
| // target memory location. |
| emscripten_atomic_wait_async__postset: `if (!Atomics.waitAsync || (typeof navigator !== 'undefined' && navigator.userAgent && jstoi_q((navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./)||[])[2]) < 91)) { |
| let __Atomics_waitAsyncAddresses = [/*[i32a, index, value, maxWaitMilliseconds, promiseResolve]*/]; |
| function __Atomics_pollWaitAsyncAddresses() { |
| let now = performance.now(); |
| let l = __Atomics_waitAsyncAddresses.length; |
| for (let i = 0; i < l; ++i) { |
| let a = __Atomics_waitAsyncAddresses[i]; |
| let expired = (now > a[3]); |
| let awoken = (Atomics.load(a[0], a[1]) != a[2]); |
| if (expired || awoken) { |
| __Atomics_waitAsyncAddresses[i--] = __Atomics_waitAsyncAddresses[--l]; |
| __Atomics_waitAsyncAddresses.length = l; |
| a[4](awoken ? 'ok': 'timed-out'); |
| } |
| } |
| if (l) { |
| // If we still have addresses to wait, loop the timeout handler to continue polling. |
| setTimeout(__Atomics_pollWaitAsyncAddresses, 10); |
| } |
| } |
| #if ASSERTIONS |
| if (!ENVIRONMENT_IS_WASM_WORKER) err('Current environment does not support Atomics.waitAsync(): polyfilling it, but this is going to be suboptimal.'); |
| #endif |
| Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => { |
| let val = Atomics.load(i32a, index); |
| if (val != value) return { async: false, value: 'not-equal' }; |
| if (maxWaitMilliseconds <= 0) return { async: false, value: 'timed-out' }; |
| maxWaitMilliseconds = performance.now() + (maxWaitMilliseconds || Infinity); |
| let promiseResolve; |
| let promise = new Promise((resolve) => { promiseResolve = resolve; }); |
| if (!__Atomics_waitAsyncAddresses[0]) setTimeout(__Atomics_pollWaitAsyncAddresses, 10); |
| __Atomics_waitAsyncAddresses.push([i32a, index, value, maxWaitMilliseconds, promiseResolve]); |
| return { async: true, value: promise }; |
| }; |
| }`, |
| |
| // These dependencies are artificial, issued so that we still get the |
| // waitAsync polyfill emitted if code only calls |
| // emscripten_lock/semaphore_async_acquire() but not |
| // emscripten_atomic_wait_async() directly. |
| emscripten_lock_async_acquire__deps: ['emscripten_atomic_wait_async'], |
| emscripten_semaphore_async_acquire__deps: ['emscripten_atomic_wait_async'], |
| |
| #endif |
| |
| $liveAtomicWaitAsyncs: '{}', |
| $liveAtomicWaitAsyncCounter: '0', |
| |
| emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$jstoi_q'], |
| emscripten_atomic_wait_async: (addr, val, asyncWaitFinished, userData, maxWaitMilliseconds) => { |
| let wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds); |
| if (!wait.async) return atomicWaitStates.indexOf(wait.value); |
| // Increment waitAsync generation counter, account for wraparound in case |
| // application does huge amounts of waitAsyncs per second (not sure if |
| // possible?) |
| // Valid counterrange: 0...2^31-1 |
| let counter = liveAtomicWaitAsyncCounter; |
| liveAtomicWaitAsyncCounter = Math.max(0, (liveAtomicWaitAsyncCounter+1)|0); |
| liveAtomicWaitAsyncs[counter] = addr; |
| wait.value.then((value) => { |
| if (liveAtomicWaitAsyncs[counter]) { |
| delete liveAtomicWaitAsyncs[counter]; |
| {{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(addr, val, atomicWaitStates.indexOf(value), userData); |
| } |
| }); |
| return -counter; |
| }, |
| |
| emscripten_atomic_cancel_wait_async__deps: ['$liveAtomicWaitAsyncs'], |
| emscripten_atomic_cancel_wait_async: (waitToken) => { |
| #if ASSERTIONS |
| if (waitToken == {{{ cDefs.ATOMICS_WAIT_NOT_EQUAL }}}) { |
| warnOnce('Attempted to call emscripten_atomic_cancel_wait_async() with a value ATOMICS_WAIT_NOT_EQUAL (1) that is not a valid wait token! Check success in return value from call to emscripten_atomic_wait_async()'); |
| } else if (waitToken == {{{ cDefs.ATOMICS_WAIT_TIMED_OUT }}}) { |
| warnOnce('Attempted to call emscripten_atomic_cancel_wait_async() with a value ATOMICS_WAIT_TIMED_OUT (2) that is not a valid wait token! Check success in return value from call to emscripten_atomic_wait_async()'); |
| } else if (waitToken > 0) { |
| warnOnce(`Attempted to call emscripten_atomic_cancel_wait_async() with an invalid wait token value ${waitToken}`); |
| } |
| #endif |
| var address = liveAtomicWaitAsyncs[waitToken]; |
| if (address) { |
| // Notify the waitAsync waiters on the memory location, so that JavaScript |
| // garbage collection can occur. |
| // See https://github.com/WebAssembly/threads/issues/176 |
| // This has the unfortunate effect of causing spurious wakeup of all other |
| // waiters at the address (which causes a small performance loss). |
| Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}}); |
| delete liveAtomicWaitAsyncs[waitToken]; |
| return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; |
| } |
| // This waitToken does not exist. |
| return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; |
| }, |
| |
| emscripten_atomic_cancel_all_wait_asyncs__deps: ['$liveAtomicWaitAsyncs'], |
| emscripten_atomic_cancel_all_wait_asyncs: () => { |
| let waitAsyncs = Object.values(liveAtomicWaitAsyncs); |
| waitAsyncs.forEach((address) => { |
| Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}}); |
| }); |
| liveAtomicWaitAsyncs = {}; |
| return waitAsyncs.length; |
| }, |
| |
| emscripten_atomic_cancel_all_wait_asyncs_at_address__deps: ['$liveAtomicWaitAsyncs'], |
| emscripten_atomic_cancel_all_wait_asyncs_at_address: (address) => { |
| let numCancelled = 0; |
| Object.keys(liveAtomicWaitAsyncs).forEach((waitToken) => { |
| if (liveAtomicWaitAsyncs[waitToken] == address) { |
| Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}}); |
| delete liveAtomicWaitAsyncs[waitToken]; |
| numCancelled++; |
| } |
| }); |
| return numCancelled; |
| }, |
| |
| emscripten_navigator_hardware_concurrency: () => { |
| #if ENVIRONMENT_MAY_BE_NODE |
| if (ENVIRONMENT_IS_NODE) return require('os').cpus().length; |
| #endif |
| return navigator['hardwareConcurrency']; |
| }, |
| |
| emscripten_atomics_is_lock_free: (width) => { |
| return Atomics.isLockFree(width); |
| }, |
| |
| emscripten_lock_async_acquire: (lock, asyncWaitFinished, userData, maxWaitMilliseconds) => { |
| let dispatch = (val, ret) => { |
| setTimeout(() => { |
| {{{ makeDynCall('viiii', 'asyncWaitFinished') }}}(lock, val, /*waitResult=*/ret, userData); |
| }, 0); |
| }; |
| let tryAcquireLock = () => { |
| do { |
| var val = Atomics.compareExchange(HEAP32, lock >> 2, 0/*zero represents lock being free*/, 1/*one represents lock being acquired*/); |
| if (!val) return dispatch(0, 0/*'ok'*/); |
| var wait = Atomics.waitAsync(HEAP32, lock >> 2, val, maxWaitMilliseconds); |
| } while (wait.value === 'not-equal'); |
| #if ASSERTIONS |
| assert(wait.async || wait.value === 'timed-out'); |
| #endif |
| if (wait.async) wait.value.then(tryAcquireLock); |
| else dispatch(val, 2/*'timed-out'*/); |
| }; |
| tryAcquireLock(); |
| }, |
| |
| emscripten_semaphore_async_acquire: (sem, num, asyncWaitFinished, userData, maxWaitMilliseconds) => { |
| let dispatch = (idx, ret) => { |
| setTimeout(() => { |
| {{{ makeDynCall('viiii', 'asyncWaitFinished') }}}(sem, /*val=*/idx, /*waitResult=*/ret, userData); |
| }, 0); |
| }; |
| let tryAcquireSemaphore = () => { |
| let val = num; |
| do { |
| let ret = Atomics.compareExchange(HEAP32, sem >> 2, |
| val, /* We expect this many semaphore resoures to be available*/ |
| val - num /* Acquire 'num' of them */); |
| if (ret == val) return dispatch(ret/*index of resource acquired*/, 0/*'ok'*/); |
| val = ret; |
| let wait = Atomics.waitAsync(HEAP32, sem >> 2, ret, maxWaitMilliseconds); |
| } while (wait.value === 'not-equal'); |
| #if ASSERTIONS |
| assert(wait.async || wait.value === 'timed-out'); |
| #endif |
| if (wait.async) wait.value.then(tryAcquireSemaphore); |
| else dispatch(-1/*idx*/, 2/*'timed-out'*/); |
| }; |
| tryAcquireSemaphore(); |
| } |
| }); |