| |
| |
| |
| |
| |
|
|
| |
| |
|
|
| 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 = []; |
|
|
| |
| |
| const CHECK_DEPS = process.env.EMCC_CHECK_DEPS; |
|
|
| |
| |
| |
| |
| const proxiedFunctionTable = []; |
|
|
| |
| function mangleCSymbolName(f) { |
| if (f === '__main_argc_argv') { |
| f = 'main'; |
| } |
| return f[0] == '$' ? f.slice(1) : '_' + f; |
| } |
|
|
| |
| 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 + "'"; |
| } |
|
|
| |
| |
| 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(',') + ']'; |
| } |
|
|
| |
| 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); |
| |
| |
| 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; |
| } |
| |
| |
| if (MAIN_MODULE && symName.startsWith('invoke_')) { |
| return true; |
| } |
| return false; |
| } |
|
|
| function getTransitiveDeps(symbol) { |
| |
| 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), true, fileName); |
| } |
|
|
| function preJS() { |
| let result = ''; |
| for (const fileName of PRE_JS_FILES) { |
| result += getIncludeFile(fileName); |
| } |
| return result; |
| } |
|
|
| |
| |
| 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([ |
| |
| '$PThread', |
| '$SDL', |
| '$GLUT', |
| '$GLEW', |
| '$Browser', |
| '$AL', |
| '$GL', |
| '$IDBStore', |
| |
| '$polyfillWaitAsync', |
| '$GLImmediateSetup', |
| '$emscriptenGetAudioObject', |
| |
| '$bigintToI53Checked', |
| '$convertI32PairToI53Checked', |
| 'setTempRet0', |
| ]); |
|
|
| |
| |
| |
| |
| |
| 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) { |
| |
| |
| |
| |
| |
| |
| 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) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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()) { |
| |
| |
| 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)) { |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| const await_ = isAsyncFunction ? 'await ' : ''; |
| const orig_async_ = async_; |
| async_ = isAsyncFunction ? 'async ' : async_; |
| if (oneliner) { |
| |
| |
| |
| 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')}; |
| }`; |
| } |
|
|
| |
| |
| 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`; |
| }); |
| } |
|
|
| |
| |
| 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) { |
| |
| |
| |
| |
| |
| snippet = snippet.toString().replace(/\r\n/gm, '\n'); |
|
|
| |
| |
| if (snippet.startsWith(symbol)) { |
| snippet = 'function ' + snippet; |
| } |
|
|
| if (isStub) { |
| return snippet; |
| } |
|
|
| |
| 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) { |
| |
| |
| 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) { |
| |
| |
| |
| |
| |
| 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)) { |
| |
| const num = +symbol.split('_').slice(-1)[0]; |
| compileTimeContext.addCxaCatch(num); |
| } |
| |
| |
| } |
|
|
| function addFromLibrary(symbol, dependent) { |
| |
| |
| if (isDecorator(symbol)) { |
| return; |
| } |
|
|
| |
| |
| 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') { |
| |
| |
| |
| deps.push('setTempRet0'); |
| } |
|
|
| const isAsyncFunction = LibraryManager.library[symbol + '__async']; |
| if (ASYNCIFY && isAsyncFunction) { |
| asyncFuncs.push(symbol); |
| } |
|
|
| if (symbolsOnly) { |
| if (LibraryManager.library.hasOwnProperty(symbol)) { |
| |
| var transitiveDeps = getTransitiveDeps(symbol); |
| symbolDeps[symbol] = transitiveDeps.filter( |
| (d) => !isJsOnlySymbol(d) && !(d in LibraryManager.library), |
| ); |
| } |
| return; |
| } |
|
|
| |
| |
| |
| 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'); |
| } |
| } |
|
|
| |
| var stubFunctionBody = `abort('missing function: ${symbol}');` |
| if (MAIN_MODULE) { |
| |
| |
| |
| let target = `wasmImports['${symbol}']`; |
| if (ASYNCIFY) { |
| |
| |
| 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']; |
| |
| 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) { |
| |
| |
| const postsetString = typeof postset == 'function' ? postset() : postset; |
| if (postsetString && !addedLibraryItems[postsetId]) { |
| addedLibraryItems[postsetId] = true; |
| postSets.push(postsetString + ';'); |
| } |
| } |
|
|
| if (LibraryManager.isAlias(snippet)) { |
| |
| |
| |
| 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); |
| |
| |
| |
| |
| |
| 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/))) { |
| |
| 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) { |
| |
| if (typeof dep == 'function') { |
| return dep(); |
| } |
| |
| |
| |
| 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) { |
| |
| if ((USE_ASAN || USE_LSAN) && LibraryManager.library[symbol + '__noleakcheck']) { |
| contentText = modifyJSFunction( |
| snippet, |
| (args, body) => `(${args}) => noLeakCheck(() => {${body}})`, |
| ); |
| deps.push('$noLeakCheck'); |
| } else { |
| contentText = snippet; |
| } |
| |
| |
| |
| if (contentText.match(/^\s*([^}]*)\s*=>/s)) { |
| |
| contentText = `var ${mangled} = ` + contentText + ';'; |
| } else if (contentText.startsWith('class ')) { |
| |
| contentText = contentText.replace(/^class(?:\s+(?!extends\b)[^{\s]+)?/, `class ${mangled}`); |
| } else { |
| |
| contentText = contentText.replace(/function(?:\s+([^(]+))?\s*\(/, `function ${mangled}(`); |
| } |
| } else if (typeof snippet == 'string' && snippet.startsWith(';')) { |
| |
| |
| |
| |
| contentText = 'var ' + mangled + snippet; |
| if (snippet[snippet.length - 1] != ';' && snippet[snippet.length - 1] != '}') { |
| contentText += ';'; |
| } |
| } else if (typeof snippet == 'undefined') { |
| |
| |
| |
| |
| if (isNativeAlias) { |
| contentText = ''; |
| } else { |
| contentText = `var ${mangled};`; |
| } |
| } else { |
| |
| |
| |
| |
| 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) { |
| |
| |
| contentText = 'export ' + contentText; |
| } |
|
|
| |
| 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;`; |
| } |
| } |
|
|
| |
| |
| 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 ')} |
| ]; |
| `); |
| } |
|
|
| |
| |
| |
| |
| 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, |
| }); |
|
|