Buckets:
| /** | |
| * @license | |
| * Copyright 2010 The Emscripten Authors | |
| * SPDX-License-Identifier: MIT | |
| */ | |
| // Convert analyzed data to javascript. Everything has already been calculated | |
| // before this stage, which just does the final conversion to JavaScript. | |
| import assert from 'node:assert'; | |
| import * as fs from 'node:fs/promises'; | |
| import { | |
| ATMODULES, | |
| ATEXITS, | |
| ATINITS, | |
| ATPOSTCTORS, | |
| ATPRERUNS, | |
| ATMAINS, | |
| ATPOSTRUNS, | |
| defineI64Param, | |
| indentify, | |
| makeReturn64, | |
| modifyJSFunction, | |
| preprocess, | |
| processMacros, | |
| receiveI64ParamAsI53, | |
| } from './parseTools.mjs'; | |
| import { | |
| addToCompileTimeContext, | |
| debugLog, | |
| error, | |
| errorOccured, | |
| isDecorator, | |
| isJsOnlySymbol, | |
| compileTimeContext, | |
| readFile, | |
| runInMacroContext, | |
| warn, | |
| warnOnce, | |
| warningOccured, | |
| localFile, | |
| timer, | |
| } from './utility.mjs'; | |
| import {LibraryManager, librarySymbols, nativeAliases} from './modules.mjs'; | |
| const addedLibraryItems = {}; | |
| const extraLibraryFuncs = []; | |
| // Experimental feature to check for invalid __deps entries. | |
| // See `EMCC_CHECK_DEPS` in in the environment to try it out. | |
| const CHECK_DEPS = process.env.EMCC_CHECK_DEPS; | |
| // Some JS-implemented library functions are proxied to be called on the main | |
| // browser thread, if the Emscripten runtime is executing in a Web Worker. | |
| // Each such proxied function is identified via an ordinal number (this is not | |
| // the same namespace as function pointers in general). | |
| const proxiedFunctionTable = []; | |
| // Mangles the given C/JS side function name to assembly level function name (adds an underscore) | |
| function mangleCSymbolName(f) { | |
| if (f === '__main_argc_argv') { | |
| f = 'main'; | |
| } | |
| return f[0] == '$' ? f.slice(1) : '_' + f; | |
| } | |
| // Splits out items that pass filter. Returns also the original sans the filtered | |
| function splitter(array, filter) { | |
| const splitOut = array.filter(filter); | |
| const leftIn = array.filter((x) => !filter(x)); | |
| return {leftIn, splitOut}; | |
| } | |
| function escapeJSONKey(x) { | |
| if (/^[\d\w_]+$/.exec(x) || x[0] === '"' || x[0] === "'") return x; | |
| assert(!x.includes("'"), 'cannot have internal single quotes in keys: ' + x); | |
| return "'" + x + "'"; | |
| } | |
| // JSON.stringify will completely omit function objects. This function is | |
| // similar but preserves functions. | |
| function stringifyWithFunctions(obj) { | |
| if (typeof obj == 'function') return obj.toString(); | |
| if (obj === null || typeof obj != 'object') return JSON.stringify(obj); | |
| if (Array.isArray(obj)) { | |
| return '[' + obj.map(stringifyWithFunctions).join(',') + ']'; | |
| } | |
| // preserve the type of the object if it is one of [Map, Set, WeakMap, WeakSet]. | |
| const builtinContainers = runInMacroContext('[Map, Set, WeakMap, WeakSet]', { | |
| filename: '<internal>', | |
| }); | |
| for (const container of builtinContainers) { | |
| if (obj instanceof container) { | |
| const className = container.name; | |
| assert(!obj.size, `cannot stringify ${className} with data`); | |
| return `new ${className}`; | |
| } | |
| } | |
| var rtn = '{\n'; | |
| for (const [key, value] of Object.entries(obj)) { | |
| var str = stringifyWithFunctions(value); | |
| // Handle JS method syntax where the function property starts with its own | |
| // name. e.g. `foo(a) {}` (or `async foo(a) {}`) | |
| if (typeof value === 'function' && (str.startsWith(key) || str.startsWith('async ' + key))) { | |
| rtn += str + ',\n'; | |
| } else { | |
| rtn += `${escapeJSONKey(key)}:${str},\n`; | |
| } | |
| } | |
| return rtn + '}'; | |
| } | |
| function isDefined(symName) { | |
| if (WASM_EXPORTS.has(symName) || SIDE_MODULE_EXPORTS.has(symName)) { | |
| return true; | |
| } | |
| if (symName == '__main_argc_argv' && SIDE_MODULE_EXPORTS.has('main')) { | |
| return true; | |
| } | |
| // 'invoke_' symbols are created at runtime in library_dylink.py so can | |
| // always be considered as defined. | |
| if (MAIN_MODULE && symName.startsWith('invoke_')) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| function getTransitiveDeps(symbol) { | |
| // TODO(sbc): Use some kind of cache to avoid quadratic behaviour here. | |
| const transitiveDeps = new Set(); | |
| const seen = new Set(); | |
| const toVisit = [symbol]; | |
| while (toVisit.length) { | |
| const sym = toVisit.pop(); | |
| if (!seen.has(sym)) { | |
| let directDeps = LibraryManager.library[sym + '__deps'] || []; | |
| directDeps = directDeps.filter((d) => typeof d === 'string'); | |
| for (const dep of directDeps) { | |
| if (!transitiveDeps.has(dep)) { | |
| debugLog(`adding dependency ${symbol} -> ${dep}`); | |
| } | |
| transitiveDeps.add(dep); | |
| toVisit.push(dep); | |
| } | |
| seen.add(sym); | |
| } | |
| } | |
| return Array.from(transitiveDeps); | |
| } | |
| function shouldPreprocess(fileName) { | |
| var content = readFile(fileName).trim(); | |
| return content.startsWith('#preprocess\n') || content.startsWith('#preprocess\r\n'); | |
| } | |
| function getIncludeFile(fileName, alwaysPreprocess, shortName) { | |
| shortName ??= fileName; | |
| let result = `// include: ${shortName}\n`; | |
| const doPreprocess = alwaysPreprocess || shouldPreprocess(fileName); | |
| if (doPreprocess) { | |
| result += processMacros(preprocess(fileName), fileName); | |
| } else { | |
| result += readFile(fileName); | |
| } | |
| result += `// end include: ${shortName}\n`; | |
| return result; | |
| } | |
| function getSystemIncludeFile(fileName) { | |
| return getIncludeFile(localFile(fileName), /*alwaysPreprocess=*/ true, /*shortName=*/ fileName); | |
| } | |
| function preJS() { | |
| let result = ''; | |
| for (const fileName of PRE_JS_FILES) { | |
| result += getIncludeFile(fileName); | |
| } | |
| return result; | |
| } | |
| // Certain library functions have specific indirect dependencies. See the | |
| // comments alongside eaach of these. | |
| const checkDependenciesSkip = new Set([ | |
| '_mmap_js', | |
| '_emscripten_throw_longjmp', | |
| '_emscripten_receive_on_main_thread_js', | |
| 'emscripten_start_fetch', | |
| 'emscripten_start_wasm_audio_worklet_thread_async', | |
| ]); | |
| const checkDependenciesIgnore = new Set([ | |
| // These are added in bulk to whole library files are so are not precise | |
| '$PThread', | |
| '$SDL', | |
| '$GLUT', | |
| '$GLEW', | |
| '$Browser', | |
| '$AL', | |
| '$GL', | |
| '$IDBStore', | |
| // These are added purely for their side effects | |
| '$polyfillWaitAsync', | |
| '$GLImmediateSetup', | |
| '$emscriptenGetAudioObject', | |
| // These get conservatively injected via i53ConversionDeps | |
| '$bigintToI53Checked', | |
| '$convertI32PairToI53Checked', | |
| 'setTempRet0', | |
| ]); | |
| /** | |
| * Hacky attempt to find unused `__deps` entries. This is not enabled by default | |
| * but can be enabled by setting CHECK_DEPS above. | |
| * TODO: Use a more precise method such as tokenising using acorn. | |
| */ | |
| function checkDependencies(symbol, snippet, deps, postset) { | |
| if (checkDependenciesSkip.has(symbol)) { | |
| return; | |
| } | |
| for (const dep of deps) { | |
| if (typeof dep === 'function') { | |
| continue; | |
| } | |
| if (checkDependenciesIgnore.has(dep)) { | |
| continue; | |
| } | |
| const mangled = mangleCSymbolName(dep); | |
| if (!snippet.includes(mangled) && (!postset || !postset.includes(mangled))) { | |
| error(`${symbol}: unused dependency: ${dep}`); | |
| } | |
| } | |
| } | |
| function addImplicitDeps(snippet, deps) { | |
| // There are some common dependencies that we inject automatically by | |
| // conservatively scanning the input functions for their usage. | |
| // Specifically, these are dependencies that are very common and would be | |
| // burdensome to add manually to all functions. | |
| // The first four are deps that are automatically/conditionally added | |
| // by the {{{ makeDynCall }}}, and {{{ runtimeKeepalivePush/Pop }}} macros. | |
| const autoDeps = [ | |
| 'getDynCaller', | |
| 'getWasmTableEntry', | |
| 'runtimeKeepalivePush', | |
| 'runtimeKeepalivePop', | |
| 'UTF8ToString', | |
| ]; | |
| for (const dep of autoDeps) { | |
| if (snippet.includes(dep + '(')) { | |
| deps.push('$' + dep); | |
| } | |
| } | |
| } | |
| function sigToArgs(sig) { | |
| const args = [] | |
| for (var i = 1; i < sig.length; i++) { | |
| args.push(`a${i}`); | |
| } | |
| return args.join(','); | |
| } | |
| function handleI64Signatures(symbol, snippet, sig, i53abi, isAsyncFunction) { | |
| // Handle i64 parameters and return values. | |
| // | |
| // When WASM_BIGINT is enabled these arrive as BigInt values which we | |
| // convert to int53 JS numbers. If necessary, we also convert the return | |
| // value back into a BigInt. | |
| // | |
| // When WASM_BIGINT is not enabled we receive i64 values as a pair of i32 | |
| // numbers which is converted to single int53 number. In necessary, we also | |
| // split the return value into a pair of i32 numbers. | |
| return modifyJSFunction(snippet, (args, body, async_, oneliner) => { | |
| let argLines = args.split('\n'); | |
| argLines = argLines.map((line) => line.split('//')[0]); | |
| const argNames = argLines | |
| .join(' ') | |
| .split(',') | |
| .map((name) => name.trim()); | |
| const newArgs = []; | |
| let argConversions = ''; | |
| if (sig.length > argNames.length + 1) { | |
| error(`handleI64Signatures: signature '${sig}' too long for ${symbol}(${argNames.join(', ')})`); | |
| return snippet; | |
| } | |
| for (const [i, name] of argNames.entries()) { | |
| // If sig is shorter than argNames list then argType will be undefined | |
| // here, which will result in the default case below. | |
| const argType = sig[i + 1]; | |
| if (WASM_BIGINT && ((MEMORY64 && argType == 'p') || (i53abi && argType == 'j'))) { | |
| argConversions += ` ${receiveI64ParamAsI53(name, undefined, false)}\n`; | |
| } else { | |
| if (argType == 'j' && i53abi) { | |
| argConversions += ` ${receiveI64ParamAsI53(name, undefined, false)}\n`; | |
| newArgs.push(defineI64Param(name)); | |
| } else if (argType == 'p' && CAN_ADDRESS_2GB) { | |
| argConversions += ` ${name} >>>= 0;\n`; | |
| newArgs.push(name); | |
| } else { | |
| newArgs.push(name); | |
| } | |
| } | |
| } | |
| if (!WASM_BIGINT) { | |
| args = newArgs.join(','); | |
| } | |
| if ((sig[0] == 'j' && i53abi) || (sig[0] == 'p' && MEMORY64)) { | |
| // For functions that where we need to mutate the return value, we | |
| // also need to wrap the body in an inner function. | |
| // If the inner function is marked as `__async` then we need to `await` | |
| // the result before casting it to BigInt. Note that we use the `__async` | |
| // attribute here rather than the presence of the `async` JS keyword | |
| // because this is what tells us that the function is going to return | |
| // a promise. i.e. we support async functions that return promises but | |
| // are not marked with the `async` keyword (the latter is only necessary | |
| // if the function uses the `await` keyword)) | |
| const await_ = isAsyncFunction ? 'await ' : ''; | |
| const orig_async_ = async_; | |
| async_ = isAsyncFunction ? 'async ' : async_; | |
| if (oneliner) { | |
| // Special case for abort(), this a noreturn function and but closure | |
| // compiler doesn't have a way to express that, so it complains if we | |
| // do `BigInt(abort(..))`. | |
| if (body.startsWith('abort(')) { | |
| return snippet; | |
| } | |
| if (argConversions) { | |
| return `${async_}(${args}) => { | |
| ${argConversions} | |
| return ${makeReturn64(await_ + body)}; | |
| }`; | |
| } | |
| return `${async_}(${args}) => ${makeReturn64(await_ + body)};`; | |
| } | |
| return `\ | |
| ${async_}function(${args}) { | |
| ${argConversions} | |
| var ret = (${orig_async_}() => { ${body} })(); | |
| return ${makeReturn64(await_ + 'ret')}; | |
| }`; | |
| } | |
| // Otherwise no inner function is needed and we covert the arguments | |
| // before executing the function body. | |
| if (oneliner) { | |
| body = `return ${body}`; | |
| } | |
| return `\ | |
| ${async_}function(${args}) { | |
| ${argConversions} | |
| ${body}; | |
| }`; | |
| }); | |
| } | |
| function handleAsyncFunction(snippet, sig) { | |
| const return64 = sig && (MEMORY64 && sig.startsWith('p') || sig.startsWith('j')) | |
| let handleAsync = 'Asyncify.handleAsync(innerFunc)' | |
| if (return64 && ASYNCIFY == 1) { | |
| handleAsync = makeReturn64(handleAsync); | |
| } | |
| return modifyJSFunction(snippet, (args, body, async_, oneliner) => { | |
| if (!oneliner) { | |
| body = `{\n${body}\n}`; | |
| } | |
| return `\ | |
| function(${args}) { | |
| let innerFunc = ${async_} () => ${body}; | |
| return ${handleAsync}; | |
| }\n`; | |
| }); | |
| } | |
| // The three different inter-thread proxying methods. | |
| // See system/lib/pthread/proxying.c | |
| const PROXY_ASYNC = 0; | |
| const PROXY_SYNC = 1; | |
| const PROXY_SYNC_ASYNC = 2; | |
| export async function runJSify(outputFile, symbolsOnly) { | |
| const libraryItems = []; | |
| const symbolDeps = {}; | |
| const asyncFuncs = []; | |
| let postSets = []; | |
| LibraryManager.load(); | |
| let outputHandle = process.stdout; | |
| if (outputFile) { | |
| outputHandle = await fs.open(outputFile, 'w'); | |
| } | |
| async function writeOutput(str) { | |
| await outputHandle.write(str + '\n'); | |
| } | |
| const symbolsNeeded = DEFAULT_LIBRARY_FUNCS_TO_INCLUDE; | |
| symbolsNeeded.push(...extraLibraryFuncs); | |
| for (const sym of EXPORTED_RUNTIME_METHODS) { | |
| if ('$' + sym in LibraryManager.library) { | |
| symbolsNeeded.push('$' + sym); | |
| } | |
| } | |
| for (const key of Object.keys(LibraryManager.library)) { | |
| if (!isDecorator(key)) { | |
| if (INCLUDE_FULL_LIBRARY || EXPORTED_FUNCTIONS.has(mangleCSymbolName(key))) { | |
| symbolsNeeded.push(key); | |
| } | |
| } | |
| } | |
| function processLibraryFunction(snippet, symbol, mangled, deps, isStub) { | |
| // It is possible that when printing the function as a string on Windows, | |
| // the js interpreter we are in returns the string with Windows line endings | |
| // \r\n. This is undesirable, since line endings are managed in the form \n | |
| // in the output for binary file writes, so make sure the endings are | |
| // uniform. | |
| snippet = snippet.toString().replace(/\r\n/gm, '\n'); | |
| // Is this a shorthand `foo() {}` method syntax? | |
| // If so, prepend a function keyword so that it's valid syntax when extracted. | |
| if (snippet.startsWith(symbol)) { | |
| snippet = 'function ' + snippet; | |
| } | |
| if (isStub) { | |
| return snippet; | |
| } | |
| // apply LIBRARY_DEBUG if relevant | |
| if (LIBRARY_DEBUG && !isJsOnlySymbol(symbol)) { | |
| snippet = modifyJSFunction(snippet, (args, body, _async, oneliner) => { | |
| var run_func; | |
| if (oneliner) { | |
| run_func = `var ret = ${body}`; | |
| } else { | |
| run_func = `var ret = (() => { ${body} })();`; | |
| } | |
| return `\ | |
| function(${args}) { | |
| dbg("[library call:${mangled}: " + Array.prototype.slice.call(arguments).map(prettyPrint) + "]"); | |
| ${run_func} | |
| dbg(" [ return:" + prettyPrint(ret)); | |
| return ret; | |
| }`; | |
| }); | |
| } | |
| const sig = LibraryManager.library[symbol + '__sig']; | |
| const isAsyncFunction = ASYNCIFY && LibraryManager.library[symbol + '__async']; | |
| const i53abi = LibraryManager.library[symbol + '__i53abi']; | |
| if (i53abi) { | |
| if (!sig) { | |
| error(`JS library error: '__i53abi' decorator requires '__sig' decorator: '${symbol}'`); | |
| } | |
| if (!sig.includes('j')) { | |
| error(`JS library error: '__i53abi' only makes sense when '__sig' includes 'j' (int64): '${symbol}'`); | |
| } | |
| } | |
| if ( | |
| sig && | |
| ((i53abi && sig.includes('j')) || ((MEMORY64 || CAN_ADDRESS_2GB) && sig.includes('p'))) | |
| ) { | |
| snippet = handleI64Signatures(symbol, snippet, sig, i53abi, isAsyncFunction); | |
| compileTimeContext.i53ConversionDeps.forEach((d) => deps.push(d)); | |
| } | |
| if (ASYNCIFY && isAsyncFunction == 'auto') { | |
| snippet = handleAsyncFunction(snippet, sig); | |
| } | |
| const proxyingMode = LibraryManager.library[symbol + '__proxy']; | |
| if (proxyingMode) { | |
| if (!['sync', 'async', 'none'].includes(proxyingMode)) { | |
| error(`JS library error: invalid proxying mode '${symbol}__proxy: ${proxyingMode}' specified`); | |
| } | |
| if (SHARED_MEMORY && proxyingMode != 'none') { | |
| if (PTHREADS) { | |
| snippet = modifyJSFunction(snippet, (args, body, async_, oneliner) => { | |
| if (oneliner) { | |
| body = `return ${body}`; | |
| } | |
| let proxyMode = PROXY_ASYNC; | |
| if (proxyingMode === 'sync') { | |
| const isAsyncFunction = LibraryManager.library[symbol + '__async']; | |
| if (isAsyncFunction) { | |
| proxyMode = PROXY_SYNC_ASYNC; | |
| } else { | |
| proxyMode = PROXY_SYNC; | |
| } | |
| } | |
| const rtnType = sig?.[0]; | |
| const proxyFunc = | |
| MEMORY64 && rtnType == 'p' ? 'proxyToMainThreadPtr' : 'proxyToMainThread'; | |
| deps.push('$' + proxyFunc); | |
| return ` | |
| ${async_}function(${args}) { | |
| if (ENVIRONMENT_IS_PTHREAD) | |
| return ${proxyFunc}(${proxiedFunctionTable.length}, 0, ${proxyMode}${args ? ', ' : ''}${args}); | |
| ${body} | |
| }\n`; | |
| }); | |
| } else if (WASM_WORKERS && ASSERTIONS) { | |
| // In ASSERTIONS builds add runtime checks that proxied functions are not attempted to be called in Wasm Workers | |
| // (since there is no automatic proxying architecture available) | |
| snippet = modifyJSFunction( | |
| snippet, | |
| (args, body) => ` | |
| function(${args}) { | |
| assert(!ENVIRONMENT_IS_WASM_WORKER, "attempt to call proxied function '${mangled}' from a Wasm Worker (where proxying is not possible)"); | |
| ${body} | |
| }\n`, | |
| ); | |
| } | |
| proxiedFunctionTable.push(mangled); | |
| } | |
| } | |
| return snippet; | |
| } | |
| function symbolHandler(symbol) { | |
| // In LLVM, exceptions generate a set of functions of form | |
| // __cxa_find_matching_catch_1(), __cxa_find_matching_catch_2(), etc. where | |
| // the number specifies the number of arguments. In Emscripten, route all | |
| // these to a single function 'findMatchingCatch' that takes an array | |
| // of argument. | |
| if (LINK_AS_CXX && !WASM_EXCEPTIONS && symbol.startsWith('__cxa_find_matching_catch_')) { | |
| if (DISABLE_EXCEPTION_THROWING) { | |
| error( | |
| 'DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but exception catching code appears. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-exceptions (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.', | |
| ); | |
| return; | |
| } | |
| if (!(symbol in LibraryManager.library)) { | |
| // Create a new __cxa_find_matching_catch variant on demand. | |
| const num = +symbol.split('_').slice(-1)[0]; | |
| compileTimeContext.addCxaCatch(num); | |
| } | |
| // Continue, with the code below emitting the proper JavaScript based on | |
| // what we just added to the library. | |
| } | |
| function addFromLibrary(symbol, dependent) { | |
| // don't process any special identifiers. These are looked up when | |
| // processing the base name of the identifier. | |
| if (isDecorator(symbol)) { | |
| return; | |
| } | |
| // if the function was implemented in compiled code, there is no need to | |
| // include the js version | |
| if (WASM_EXPORTS.has(symbol)) { | |
| return; | |
| } | |
| if (symbol in addedLibraryItems) { | |
| return; | |
| } | |
| addedLibraryItems[symbol] = true; | |
| const deps = LibraryManager.library[symbol + '__deps'] ??= []; | |
| let sig = LibraryManager.library[symbol + '__sig']; | |
| if (!WASM_BIGINT && sig && sig[0] == 'j') { | |
| // Without WASM_BIGINT functions that return i64 depend on setTempRet0 | |
| // to return the upper 32-bits of the result. | |
| // See makeReturn64 in parseTools.py. | |
| deps.push('setTempRet0'); | |
| } | |
| const isAsyncFunction = LibraryManager.library[symbol + '__async']; | |
| if (ASYNCIFY && isAsyncFunction) { | |
| asyncFuncs.push(symbol); | |
| } | |
| if (symbolsOnly) { | |
| if (LibraryManager.library.hasOwnProperty(symbol)) { | |
| // Resolve aliases before looking up deps | |
| var transitiveDeps = getTransitiveDeps(symbol); | |
| symbolDeps[symbol] = transitiveDeps.filter( | |
| (d) => !isJsOnlySymbol(d) && !(d in LibraryManager.library), | |
| ); | |
| } | |
| return; | |
| } | |
| // This gets set to true in the case of dynamic linking for symbols that | |
| // are undefined in the main module. In this case we create a stub that | |
| // will resolve the correct symbol at runtime, or assert if its missing. | |
| let isStub = false; | |
| const mangled = mangleCSymbolName(symbol); | |
| if (!LibraryManager.library.hasOwnProperty(symbol)) { | |
| const isWeakImport = WEAK_IMPORTS.has(symbol); | |
| if (!isDefined(symbol) && !isWeakImport) { | |
| if (PROXY_TO_PTHREAD && !MAIN_MODULE && symbol == '__main_argc_argv') { | |
| error('PROXY_TO_PTHREAD proxies main() for you, but no main exists'); | |
| return; | |
| } | |
| let undefinedSym = symbol; | |
| if (symbol === '__main_argc_argv') { | |
| undefinedSym = 'main/__main_argc_argv'; | |
| } | |
| let msg = 'undefined symbol: ' + undefinedSym; | |
| if (dependent) msg += ` (referenced by ${dependent})`; | |
| if (ERROR_ON_UNDEFINED_SYMBOLS) { | |
| error(msg); | |
| warnOnce( | |
| 'To disable errors for undefined symbols use `-sERROR_ON_UNDEFINED_SYMBOLS=0`', | |
| ); | |
| warnOnce( | |
| mangled + | |
| ' may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library', | |
| ); | |
| } else if (WARN_ON_UNDEFINED_SYMBOLS) { | |
| warn(msg); | |
| } else { | |
| debugLog(msg); | |
| } | |
| if (symbol === '__main_argc_argv' && STANDALONE_WASM) { | |
| warn('To build in STANDALONE_WASM mode without a main(), use emcc --no-entry'); | |
| } | |
| } | |
| // emit a stub that will fail at runtime | |
| var stubFunctionBody = `abort('missing function: ${symbol}');` | |
| if (MAIN_MODULE) { | |
| // Create a stub for this symbol which can later be replaced by the | |
| // dynamic linker. If this stub is called before the symbol is | |
| // resolved assert in debug builds or trap in release builds. | |
| let target = `wasmImports['${symbol}']`; | |
| if (ASYNCIFY) { | |
| // See the definition of asyncifyStubs in preamble.js for why this | |
| // is needed. | |
| target = `asyncifyStubs['${symbol}']`; | |
| } | |
| let assertion = ''; | |
| if (ASSERTIONS) { | |
| assertion += `if (!${target} || ${target}.stub) abort("external symbol '${symbol}' is missing. perhaps a side module was not linked in? if this function was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment");\n`; | |
| } | |
| stubFunctionBody = assertion + `return ${target}(...args);`; | |
| } | |
| isStub = true; | |
| LibraryManager.library[symbol] = new Function('...args', stubFunctionBody); | |
| } | |
| librarySymbols.push(mangled); | |
| const original = LibraryManager.library[symbol]; | |
| let snippet = original; | |
| const isUserSymbol = LibraryManager.library[symbol + '__user']; | |
| // Check for dependencies on `__internal` symbols from user libraries. | |
| for (const dep of deps) { | |
| if (isUserSymbol && LibraryManager.library[dep + '__internal']) { | |
| warn(`user library symbol '${symbol}' depends on internal symbol '${dep}'`); | |
| } | |
| } | |
| let isFunction = typeof snippet == 'function'; | |
| let isNativeAlias = false; | |
| const postsetId = symbol + '__postset'; | |
| const postset = LibraryManager.library[postsetId]; | |
| if (postset) { | |
| // A postset is either code to run right now, or some text we should emit. | |
| // If it's code, it may return some text to emit as well. | |
| const postsetString = typeof postset == 'function' ? postset() : postset; | |
| if (postsetString && !addedLibraryItems[postsetId]) { | |
| addedLibraryItems[postsetId] = true; | |
| postSets.push(postsetString + ';'); | |
| } | |
| } | |
| if (LibraryManager.isAlias(snippet)) { | |
| // Redirection for aliases. We include the parent, and at runtime | |
| // make ourselves equal to it. This avoid having duplicate | |
| // functions with identical content. | |
| const aliasTarget = snippet; | |
| if (WASM_EXPORTS.has(aliasTarget)) { | |
| debugLog(`native alias: ${mangled} -> ${aliasTarget}`); | |
| nativeAliases[mangled] = aliasTarget; | |
| snippet = undefined; | |
| isNativeAlias = true; | |
| } else { | |
| debugLog(`js alias: ${mangled} -> ${aliasTarget}`); | |
| snippet = mangleCSymbolName(aliasTarget); | |
| // When we have an alias for another JS function we can normally | |
| // point them at the same function. However, in some cases (where | |
| // signatures are relevant and they differ between and alais and | |
| // it's target) we need to construct a forwarding function from | |
| // one to the other. | |
| const isSigRelevant = MAIN_MODULE || MEMORY64 || CAN_ADDRESS_2GB || sig?.includes('j'); | |
| const targetSig = LibraryManager.library[aliasTarget + '__sig']; | |
| if (isSigRelevant && sig && targetSig && sig != targetSig) { | |
| debugLog(`${symbol}: Alias target (${aliasTarget}) has different signature (${sig} vs ${targetSig})`) | |
| isFunction = true; | |
| snippet = `(${sigToArgs(sig)}) => ${snippet}(${sigToArgs(targetSig)})`; | |
| } | |
| } | |
| } else if (typeof snippet == 'object') { | |
| snippet = stringifyWithFunctions(snippet); | |
| addImplicitDeps(snippet, deps); | |
| } else if (typeof snippet == 'string' && (snippet.match(/^\s*\([^}]*\)\s*=>/) || snippet.match(/^function\b/))) { | |
| // Support functions that are already "stringified" | |
| isFunction = true; | |
| } | |
| if (isFunction) { | |
| snippet = processLibraryFunction(snippet, symbol, mangled, deps, isStub); | |
| addImplicitDeps(snippet, deps); | |
| if (CHECK_DEPS && !isUserSymbol) { | |
| checkDependencies(symbol, snippet, deps, postset?.toString()); | |
| } | |
| } | |
| debugLog(`adding ${symbol} (referenced by ${dependent})`); | |
| function addDependency(dep) { | |
| // dependencies can be JS functions, which we just run | |
| if (typeof dep == 'function') { | |
| return dep(); | |
| } | |
| // $noExitRuntime is special since there are conditional usages of it | |
| // in libcore.js and libpthread.js. These happen before deps are | |
| // processed so depending on it via `__deps` doesn't work. | |
| if (dep === '$noExitRuntime') { | |
| error( | |
| 'noExitRuntime cannot be referenced via __deps mechanism. Use DEFAULT_LIBRARY_FUNCS_TO_INCLUDE or EXPORTED_RUNTIME_METHODS', | |
| ); | |
| } | |
| return addFromLibrary(dep, `${symbol}, referenced by ${dependent}`); | |
| } | |
| let contentText; | |
| if (isFunction) { | |
| // Emit the body of a JS library function. | |
| if ((USE_ASAN || USE_LSAN) && LibraryManager.library[symbol + '__noleakcheck']) { | |
| contentText = modifyJSFunction( | |
| snippet, | |
| (args, body) => `(${args}) => noLeakCheck(() => {${body}})`, | |
| ); | |
| deps.push('$noLeakCheck'); | |
| } else { | |
| contentText = snippet; // Regular JS function that will be executed in the context of the calling thread. | |
| } | |
| // Give the function the correct (mangled) name. Overwrite it if it's | |
| // already named. This must happen after the last call to | |
| // modifyJSFunction which could have changed or removed the name. | |
| if (contentText.match(/^\s*([^}]*)\s*=>/s)) { | |
| // Handle arrow functions | |
| contentText = `var ${mangled} = ` + contentText + ';'; | |
| } else if (contentText.startsWith('class ')) { | |
| // Handle class declarations (which also have typeof == 'function'.) | |
| contentText = contentText.replace(/^class(?:\s+(?!extends\b)[^{\s]+)?/, `class ${mangled}`); | |
| } else { | |
| // Handle regular (non-arrow) functions | |
| contentText = contentText.replace(/function(?:\s+([^(]+))?\s*\(/, `function ${mangled}(`); | |
| } | |
| } else if (typeof snippet == 'string' && snippet.startsWith(';')) { | |
| // In JS libraries | |
| // foo: ';[code here verbatim]' | |
| // emits | |
| // 'var foo;[code here verbatim];' | |
| contentText = 'var ' + mangled + snippet; | |
| if (snippet[snippet.length - 1] != ';' && snippet[snippet.length - 1] != '}') { | |
| contentText += ';'; | |
| } | |
| } else if (typeof snippet == 'undefined') { | |
| // For JS library functions that are simply aliases of native symbols, | |
| // we don't need to generate anything here. Instead these get included | |
| // and exported alongside native symbols. | |
| // See `create_receiving` in `tools/emscripten.py`. | |
| if (isNativeAlias) { | |
| contentText = ''; | |
| } else { | |
| contentText = `var ${mangled};`; | |
| } | |
| } else { | |
| // In JS libraries | |
| // foo: '=[value]' | |
| // emits | |
| // 'var foo = [value];' | |
| if (typeof snippet == 'string' && snippet[0] == '=') { | |
| snippet = snippet.slice(1); | |
| } | |
| contentText = `var ${mangled} = ${snippet};`; | |
| } | |
| if (contentText && MODULARIZE == 'instance' && (EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled)) && !isStub) { | |
| // In MODULARIZE=instance mode mark JS library symbols are exported at | |
| // the point of declaration. | |
| contentText = 'export ' + contentText; | |
| } | |
| // Dynamic linking needs signatures to create proper wrappers. | |
| if (sig && MAIN_MODULE) { | |
| if (!WASM_BIGINT) { | |
| sig = sig[0].replace('j', 'i') + sig.slice(1).replace(/j/g, 'ii'); | |
| } | |
| contentText += `\n${mangled}.sig = '${sig}';`; | |
| } | |
| if (ASYNCIFY && isAsyncFunction) { | |
| assert(isFunction); | |
| contentText += `\n${mangled}.isAsync = true;`; | |
| } | |
| if (isStub) { | |
| contentText += `\n${mangled}.stub = true;`; | |
| if (ASYNCIFY && MAIN_MODULE) { | |
| contentText += `\nasyncifyStubs['${symbol}'] = undefined;`; | |
| } | |
| } | |
| // Add the docs if they exist and if we are actually emitting a declaration. | |
| // See the TODO about wasmTable above. | |
| let docs = LibraryManager.library[symbol + '__docs']; | |
| let commentText = ''; | |
| if (contentText != '' && docs) { | |
| commentText += docs + '\n'; | |
| } | |
| if (EMIT_TSD) { | |
| LibraryManager.libraryDefinitions[mangled] = { | |
| docs: docs ?? null, | |
| snippet: snippet ?? null, | |
| }; | |
| } | |
| const depsText = deps | |
| ? deps | |
| .map(addDependency) | |
| .filter((x) => x != '') | |
| .join('\n') + '\n' | |
| : ''; | |
| return depsText + commentText + contentText; | |
| } | |
| const JS = addFromLibrary(symbol, 'root reference (e.g. compiled C/C++ code)'); | |
| libraryItems.push(JS); | |
| } | |
| function includeSystemFile(fileName) { | |
| writeOutput(getSystemIncludeFile(fileName)); | |
| } | |
| function includeFile(fileName) { | |
| writeOutput(getIncludeFile(fileName)); | |
| } | |
| function finalCombiner() { | |
| const splitPostSets = splitter(postSets, (x) => x.symbol && x.dependencies); | |
| postSets = splitPostSets.leftIn; | |
| const orderedPostSets = splitPostSets.splitOut; | |
| let limit = orderedPostSets.length * orderedPostSets.length; | |
| for (let i = 0; i < orderedPostSets.length; i++) { | |
| for (let j = i + 1; j < orderedPostSets.length; j++) { | |
| if (orderedPostSets[j].symbol in orderedPostSets[i].dependencies) { | |
| const temp = orderedPostSets[i]; | |
| orderedPostSets[i] = orderedPostSets[j]; | |
| orderedPostSets[j] = temp; | |
| i--; | |
| limit--; | |
| assert(limit > 0, 'could not sort postsets'); | |
| break; | |
| } | |
| } | |
| } | |
| postSets.push(...orderedPostSets); | |
| const shellFile = MINIMAL_RUNTIME ? 'shell_minimal.js' : 'shell.js'; | |
| includeSystemFile(shellFile); | |
| const preFile = MINIMAL_RUNTIME ? 'preamble_minimal.js' : 'preamble.js'; | |
| includeSystemFile(preFile); | |
| writeOutput('// Begin JS library code\n'); | |
| for (const item of libraryItems.concat(postSets)) { | |
| writeOutput(indentify(item || '', 2)); | |
| } | |
| writeOutput('// End JS library code\n'); | |
| if (!MINIMAL_RUNTIME) { | |
| includeSystemFile('postlibrary.js'); | |
| } | |
| if (PTHREADS) { | |
| writeOutput(` | |
| // proxiedFunctionTable specifies the list of functions that can be called | |
| // either synchronously or asynchronously from other threads in postMessage()d | |
| // or internally queued events. This way a pthread in a Worker can synchronously | |
| // access e.g. the DOM on the main thread. | |
| var proxiedFunctionTable = [ | |
| ${proxiedFunctionTable.join(',\n ')} | |
| ]; | |
| `); | |
| } | |
| // This is the main 'post' pass. Print out the generated code | |
| // that we have here, together with the rest of the output | |
| // that we started to print out earlier (see comment on the | |
| // "Final shape that will be created"). | |
| writeOutput('// EMSCRIPTEN_END_FUNCS\n'); | |
| const postFile = MINIMAL_RUNTIME ? 'postamble_minimal.js' : 'postamble.js'; | |
| includeSystemFile(postFile); | |
| for (const fileName of POST_JS_FILES) { | |
| includeFile(fileName); | |
| } | |
| if (MODULARIZE && MODULARIZE != 'instance') { | |
| includeSystemFile('postamble_modularize.js'); | |
| } | |
| if (errorOccured()) { | |
| throw Error('Aborting compilation due to previous errors'); | |
| } | |
| writeOutput( | |
| '//FORWARDED_DATA:' + | |
| JSON.stringify({ | |
| librarySymbols, | |
| nativeAliases, | |
| warnings: warningOccured(), | |
| asyncFuncs, | |
| libraryDefinitions: LibraryManager.libraryDefinitions, | |
| ATPRERUNS: ATPRERUNS.join('\n'), | |
| ATMODULES: ATMODULES.join('\n'), | |
| ATINITS: ATINITS.join('\n'), | |
| ATPOSTCTORS: ATPOSTCTORS.join('\n'), | |
| ATMAINS: ATMAINS.join('\n'), | |
| ATPOSTRUNS: ATPOSTRUNS.join('\n'), | |
| ATEXITS: ATEXITS.join('\n'), | |
| }), | |
| ); | |
| } | |
| for (const sym of symbolsNeeded) { | |
| symbolHandler(sym); | |
| } | |
| if (symbolsOnly) { | |
| writeOutput( | |
| JSON.stringify({ | |
| deps: symbolDeps, | |
| asyncFuncs, | |
| extraLibraryFuncs, | |
| }), | |
| ); | |
| } else { | |
| timer.start('finalCombiner') | |
| finalCombiner(); | |
| timer.stop('finalCombiner') | |
| } | |
| if (errorOccured()) { | |
| throw Error('Aborting compilation due to previous errors'); | |
| } | |
| if (outputFile) await outputHandle.close(); | |
| } | |
| addToCompileTimeContext({ | |
| extraLibraryFuncs, | |
| addedLibraryItems, | |
| preJS, | |
| }); | |
Xet Storage Details
- Size:
- 34.3 kB
- Xet hash:
- 569ab9e762670bedd95209395d0fa4a79625d3d3da4e3aaa550ff3705933b957
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.