/** * @license * Copyright 2020 The Emscripten Authors * SPDX-License-Identifier: MIT */ addToLibrary({ // This gives correct answers for everything less than 2^{14} = 16384 // I hope nobody is contemplating functions with 16384 arguments... $uleb128EncodeWithLen__internal: true, $uleb128EncodeWithLen: (arr) => { const n = arr.length; #if ASSERTIONS assert(n < 16384); #endif // Note: this LEB128 length encoding produces extra byte for n < 128, // but we don't care as it's only used in a temporary representation. return [(n % 128) | 128, n >> 7, ...arr]; }, $wasmTypeCodes__internal: true, // Note: using template literal here instead of plain object // because jsify serializes objects w/o quotes and Closure will then // incorrectly mangle the properties. $wasmTypeCodes: `{ 'i': 0x7f, // i32 #if MEMORY64 'p': 0x7e, // i64 #else 'p': 0x7f, // i32 #endif 'j': 0x7e, // i64 'f': 0x7d, // f32 'd': 0x7c, // f64 'e': 0x6f, // externref }`, $generateTypePack__internal: true, $generateTypePack__deps: ['$uleb128EncodeWithLen', '$wasmTypeCodes'], $generateTypePack: (types) => uleb128EncodeWithLen(Array.from(types, (type) => { var code = wasmTypeCodes[type]; #if ASSERTIONS assert(code, `invalid signature char: ${type}`); #endif return code; })), #if !WASM2JS || WASM == 2 // Wraps a JS function as a wasm function with a given signature. $convertJsFunctionToWasm__deps: [ '$uleb128EncodeWithLen', '$generateTypePack' ], $convertJsFunctionToWasm: (func, sig) => { #if ASSERTIONS && !WASM_BIGINT assert(!sig.includes('j'), 'i64 not permitted in function signatures when WASM_BIGINT is disabled'); #endif // TODO: If the type reflection proposal ever makes progress we can use // it here instead of creatign a new module. var bytes = Uint8Array.of( 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") 0x01, 0x00, 0x00, 0x00, // version: 1 0x01, // Type section code // The module is static, with the exception of the type section, which is // generated based on the signature passed in. ...uleb128EncodeWithLen([ 0x01, // count: 1 0x60 /* form: func */, // param types ...generateTypePack(sig.slice(1)), // return types (for now only supporting [] if `void` and single [T] otherwise) ...generateTypePack(sig[0] === 'v' ? '' : sig[0]) ]), // The rest of the module is static 0x02, 0x07, // import section // (import "e" "f" (func 0 (type 0))) 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, 0x07, 0x05, // export section // (export "f" (func 0 (type 0))) 0x01, 0x01, 0x66, 0x00, 0x00, ); // We can compile this wasm module synchronously because it is very small. // This accepts an import (at "e.f"), that it reroutes to an export (at "f") var module = new WebAssembly.Module(bytes); var instance = new WebAssembly.Instance(module, { 'e': { 'f': func } }); var wrappedFunc = instance.exports['f']; return wrappedFunc; }, #endif // !WASM2JS && WASM != 2 $freeTableIndexes: [], // Weak map of functions in the table to their indexes, created on first use. $functionsInTableMap: undefined, $getEmptyTableSlot__deps: ['$freeTableIndexes', '$wasmTable'], $getEmptyTableSlot: () => { // Reuse a free index if there is one, otherwise grow. if (freeTableIndexes.length) { return freeTableIndexes.pop(); } #if ASSERTIONS try { #endif // Grow the table return wasmTable['grow']({{{ toIndexType('1') }}}); #if ASSERTIONS } catch (err) { if (!(err instanceof RangeError)) { throw err; } abort('Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'); } #endif }, $updateTableMap__deps: ['$getWasmTableEntry'], $updateTableMap: (offset, count) => { if (functionsInTableMap) { for (var i = offset; i < offset + count; i++) { var item = getWasmTableEntry(i); // Ignore null values. if (item) { functionsInTableMap.set(item, i); } } } }, $getFunctionAddress__deps: ['$updateTableMap', '$functionsInTableMap', '$wasmTable'], $getFunctionAddress: (func) => { // First, create the map if this is the first use. if (!functionsInTableMap) { functionsInTableMap = new WeakMap(); updateTableMap(0, {{{ from64Expr('wasmTable.length') }}}); } return functionsInTableMap.get(func) || 0; }, /** * Add a function to the table. * 'sig' parameter is required if the function being added is a JS function. */ $addFunction__docs: '/** @param {string=} sig */', $addFunction__deps: ['$getFunctionAddress', '$functionsInTableMap', '$getEmptyTableSlot', '$setWasmTableEntry', #if !WASM2JS || WASM == 2 '$convertJsFunctionToWasm', #endif #if ASSERTIONS >= 2 '$getWasmTableEntry', '$wasmTable', #endif ], $addFunction: (func, sig) => { #if ASSERTIONS assert(typeof func != 'undefined'); #endif // ASSERTIONS // Check if the function is already in the table, to ensure each function // gets a unique index. var rtn = getFunctionAddress(func); if (rtn) { return rtn; } // It's not in the table, add it now. #if ASSERTIONS >= 2 // Make sure functionsInTableMap is actually up to date, that is, that this // function is not actually in the wasm Table despite not being tracked in // functionsInTableMap. for (var i = 0; i < wasmTable.length; i++) { assert(getWasmTableEntry(i) != func, 'function in Table but not functionsInTableMap'); } #endif var ret = getEmptyTableSlot(); #if WASM2JS && WASM != 2 setWasmTableEntry(ret, func); #else // Set the new value. try { // Attempting to call this with JS function will cause table.set() to fail setWasmTableEntry(ret, func); } catch (err) { if (!(err instanceof TypeError)) { throw err; } #if ASSERTIONS assert(typeof sig != 'undefined', 'Missing signature argument to addFunction: ' + func); #endif var wrapped = convertJsFunctionToWasm(func, sig); setWasmTableEntry(ret, wrapped); } #endif functionsInTableMap.set(func, ret); return ret; }, $removeFunction__deps: ['$functionsInTableMap', '$freeTableIndexes', '$getWasmTableEntry', '$setWasmTableEntry'], $removeFunction: (index) => { functionsInTableMap.delete(getWasmTableEntry(index)); setWasmTableEntry(index, null); freeTableIndexes.push(index); }, });