| /** |
| * @license |
| * Copyright 2020 The Emscripten Authors |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| mergeInto(LibraryManager.library, { |
| // This gives correct answers for everything less than 2^{14} = 16384 |
| // I hope nobody is contemplating functions with 16384 arguments... |
| $uleb128Encode: function(n, target) { |
| #if ASSERTIONS |
| assert(n < 16384); |
| #endif |
| if (n < 128) { |
| target.push(n); |
| } else { |
| target.push((n % 128) | 128, n >> 7); |
| } |
| }, |
| |
| // Converts a signature like 'vii' into a description of the wasm types, like |
| // { parameters: ['i32', 'i32'], results: [] }. |
| $sigToWasmTypes: function(sig) { |
| var typeNames = { |
| 'i': 'i32', |
| #if MEMORY64 |
| 'j': 'i64', |
| #else |
| // i64 values will be split into two i32s. |
| 'j': 'i32', |
| #endif |
| 'f': 'f32', |
| 'd': 'f64', |
| #if MEMORY64 |
| 'p': 'i64', |
| #else |
| 'p': 'i32', |
| #endif |
| }; |
| var type = { |
| parameters: [], |
| results: sig[0] == 'v' ? [] : [typeNames[sig[0]]] |
| }; |
| for (var i = 1; i < sig.length; ++i) { |
| #if ASSERTIONS |
| assert(sig[i] in typeNames, 'invalid signature char: ' + sig[i]); |
| #endif |
| type.parameters.push(typeNames[sig[i]]); |
| #if !MEMORY64 |
| if (sig[i] === 'j') { |
| type.parameters.push('i32'); |
| } |
| #endif |
| } |
| return type; |
| }, |
| $generateFuncType__deps: ['$uleb128Encode'], |
| $generateFuncType : function(sig, target){ |
| var sigRet = sig.slice(0, 1); |
| var sigParam = sig.slice(1); |
| var typeCodes = { |
| 'i': 0x7f, // i32 |
| #if MEMORY64 |
| 'p': 0x7e, // i64 |
| #else |
| 'p': 0x7f, // i32 |
| #endif |
| 'j': 0x7e, // i64 |
| 'f': 0x7d, // f32 |
| 'd': 0x7c, // f64 |
| }; |
| |
| // Parameters, length + signatures |
| target.push(0x60 /* form: func */); |
| uleb128Encode(sigParam.length, target); |
| for (var i = 0; i < sigParam.length; ++i) { |
| #if ASSERTIONS |
| assert(sigParam[i] in typeCodes, 'invalid signature char: ' + sigParam[i]); |
| #endif |
| target.push(typeCodes[sigParam[i]]); |
| } |
| |
| // Return values, length + signatures |
| // With no multi-return in MVP, either 0 (void) or 1 (anything else) |
| if (sigRet == 'v') { |
| target.push(0x00); |
| } else { |
| target.push(0x01, typeCodes[sigRet]); |
| } |
| }, |
| // Wraps a JS function as a wasm function with a given signature. |
| $convertJsFunctionToWasm__deps: ['$uleb128Encode', '$sigToWasmTypes', '$generateFuncType'], |
| $convertJsFunctionToWasm: function(func, sig) { |
| #if WASM2JS |
| // return func; |
| #else // WASM2JS |
| |
| // If the type reflection proposal is available, use the new |
| // "WebAssembly.Function" constructor. |
| // Otherwise, construct a minimal wasm module importing the JS function and |
| // re-exporting it. |
| if (typeof WebAssembly.Function == "function") { |
| return new WebAssembly.Function(sigToWasmTypes(sig), func); |
| } |
| |
| // The module is static, with the exception of the type section, which is |
| // generated based on the signature passed in. |
| var typeSectionBody = [ |
| 0x01, // count: 1 |
| ]; |
| generateFuncType(sig, typeSectionBody); |
| |
| // Rest of the module is static |
| var bytes = [ |
| 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") |
| 0x01, 0x00, 0x00, 0x00, // version: 1 |
| 0x01, // Type section code |
| ]; |
| // Write the overall length of the type section followed by the body |
| uleb128Encode(typeSectionBody.length, bytes); |
| bytes.push.apply(bytes, typeSectionBody); |
| |
| // The rest of the module is static |
| bytes.push( |
| 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(new Uint8Array(bytes)); |
| var instance = new WebAssembly.Instance(module, { 'e': { 'f': func } }); |
| var wrappedFunc = instance.exports['f']; |
| return wrappedFunc; |
| #endif // WASM2JS |
| }, |
| |
| $freeTableIndexes: [], |
| |
| // Weak map of functions in the table to their indexes, created on first use. |
| $functionsInTableMap: undefined, |
| |
| $getEmptyTableSlot__deps: ['$freeTableIndexes'], |
| $getEmptyTableSlot: function() { |
| // Reuse a free index if there is one, otherwise grow. |
| if (freeTableIndexes.length) { |
| return freeTableIndexes.pop(); |
| } |
| // Grow the table |
| try { |
| wasmTable.grow(1); |
| } catch (err) { |
| if (!(err instanceof RangeError)) { |
| throw err; |
| } |
| throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'; |
| } |
| return wasmTable.length - 1; |
| }, |
| |
| $updateTableMap__deps: ['$getWasmTableEntry'], |
| $updateTableMap: function(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'], |
| $getFunctionAddress: function(func) { |
| // First, create the map if this is the first use. |
| if (!functionsInTableMap) { |
| functionsInTableMap = new WeakMap(); |
| updateTableMap(0, 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: ['$convertJsFunctionToWasm', '$getFunctionAddress', |
| '$functionsInTableMap', '$getEmptyTableSlot', |
| '$getWasmTableEntry', '$setWasmTableEntry'], |
| $addFunction: function(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(); |
| |
| // Set the new value. |
| try { |
| // Attempting to call this with JS function will cause of 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); |
| } |
| |
| functionsInTableMap.set(func, ret); |
| |
| return ret; |
| }, |
| |
| $removeFunction__deps: ['$functionsInTableMap', '$freeTableIndexes', '$getWasmTableEntry'], |
| $removeFunction: function(index) { |
| functionsInTableMap.delete(getWasmTableEntry(index)); |
| freeTableIndexes.push(index); |
| }, |
| }); |
| |