| /** |
| * @license |
| * Copyright 2011 The Emscripten Authors |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| // "use strict"; |
| |
| // Various namespace-like modules |
| |
| // Constructs an array ['a0', 'a1', 'a2', ..., 'a(n-1)'] |
| function genArgSequence(n) { |
| const args = []; |
| for (let i = 0; i < n; ++i) { |
| args.push('a' + i); |
| } |
| return args; |
| } |
| |
| // List of symbols that were added from the library. |
| global.librarySymbols = []; |
| |
| global.LibraryManager = { |
| library: {}, |
| structs: {}, |
| loaded: false, |
| libraries: [], |
| |
| has: function(name) { |
| return this.libraries.includes(name); |
| }, |
| |
| load: function() { |
| assert(!this.loaded); |
| this.loaded = true; |
| |
| // Core system libraries (always linked against) |
| let libraries = [ |
| 'library.js', |
| 'library_sigs.js', |
| 'library_int53.js', |
| 'library_ccall.js', |
| 'library_addfunction.js', |
| 'library_formatString.js', |
| 'library_getvalue.js', |
| 'library_math.js', |
| 'library_path.js', |
| 'library_strings.js', |
| 'library_syscall.js', |
| 'library_html5.js', |
| 'library_stack_trace.js', |
| 'library_wasi.js', |
| 'library_dylink.js', |
| 'library_makeDynCall.js', |
| 'library_eventloop.js', |
| 'library_promise.js', |
| ]; |
| |
| if (LINK_AS_CXX) { |
| if (DISABLE_EXCEPTION_THROWING && !WASM_EXCEPTIONS) { |
| libraries.push('library_exceptions_stub.js'); |
| } else { |
| libraries.push('library_exceptions.js'); |
| } |
| } |
| |
| if (!MINIMAL_RUNTIME) { |
| libraries.push('library_browser.js'); |
| libraries.push('library_wget.js'); |
| } |
| |
| if (EMSCRIPTEN_TRACING) { |
| libraries.push('library_memoryprofiler.js'); |
| } |
| |
| if (AUTODEBUG) { |
| libraries.push('library_autodebug.js'); |
| } |
| |
| if (FILESYSTEM) { |
| // Core filesystem libraries (always linked against, unless -sFILESYSTEM=0 is specified) |
| libraries = libraries.concat([ |
| 'library_fs.js', |
| 'library_memfs.js', |
| 'library_tty.js', |
| 'library_pipefs.js', // ok to include it by default since it's only used if the syscall is used |
| 'library_sockfs.js', // ok to include it by default since it's only used if the syscall is used |
| ]); |
| |
| if (NODERAWFS) { |
| // NODERAWFS requires NODEFS |
| if (!JS_LIBRARIES.includes('library_nodefs.js')) { |
| libraries.push('library_nodefs.js'); |
| } |
| libraries.push('library_noderawfs.js'); |
| // NODERAWFS overwrites library_path.js |
| libraries.push('library_nodepath.js'); |
| } |
| } else if (WASMFS) { |
| libraries.push('library_wasmfs.js'); |
| libraries.push('library_wasmfs_js_file.js'); |
| libraries.push('library_wasmfs_jsimpl.js'); |
| libraries.push('library_wasmfs_fetch.js'); |
| libraries.push('library_wasmfs_node.js'); |
| libraries.push('library_wasmfs_opfs.js'); |
| } |
| |
| // Additional JS libraries (without AUTO_JS_LIBRARIES, link to these explicitly via -lxxx.js) |
| if (AUTO_JS_LIBRARIES) { |
| libraries = libraries.concat([ |
| 'library_webgl.js', |
| 'library_html5_webgl.js', |
| 'library_openal.js', |
| 'library_glut.js', |
| 'library_xlib.js', |
| 'library_egl.js', |
| 'library_uuid.js', |
| 'library_glew.js', |
| 'library_idbstore.js', |
| 'library_async.js', |
| ]); |
| if (USE_SDL != 2) { |
| libraries.push('library_sdl.js'); |
| } |
| } else { |
| if (ASYNCIFY) { |
| libraries.push('library_async.js'); |
| } |
| if (USE_SDL == 1) { |
| libraries.push('library_sdl.js'); |
| } |
| if (USE_SDL == 2) { |
| libraries.push('library_egl.js', 'library_webgl.js', 'library_html5_webgl.js'); |
| } |
| } |
| |
| if (USE_GLFW) { |
| libraries.push('library_glfw.js'); |
| } |
| |
| if (LZ4) { |
| libraries.push('library_lz4.js'); |
| } |
| |
| if (MAX_WEBGL_VERSION >= 2) { |
| // library_webgl2.js must be included only after library_webgl.js, so if we are |
| // about to include library_webgl2.js, first squeeze in library_webgl.js. |
| libraries.push('library_webgl.js'); |
| libraries.push('library_webgl2.js'); |
| } |
| |
| if (GL_EXPLICIT_UNIFORM_LOCATION || GL_EXPLICIT_UNIFORM_BINDING) { |
| libraries.push('library_c_preprocessor.js'); |
| } |
| |
| if (LEGACY_GL_EMULATION) { |
| libraries.push('library_glemu.js'); |
| } |
| |
| if (USE_WEBGPU) { |
| libraries.push('library_webgpu.js'); |
| libraries.push('library_html5_webgpu.js'); |
| } |
| |
| if (!STRICT) { |
| libraries.push('library_legacy.js'); |
| } |
| |
| if (BOOTSTRAPPING_STRUCT_INFO) { |
| libraries = [ |
| 'library_bootstrap.js', |
| 'library_formatString.js', |
| 'library_strings.js', |
| 'library_int53.js', |
| ]; |
| } |
| |
| if (SUPPORT_BIG_ENDIAN) { |
| libraries.push('library_little_endian_heap.js'); |
| } |
| |
| // Add all user specified --js-library files to the link. |
| // These must be added last after all Emscripten-provided system libraries |
| // above, so that users can override built-in JS library symbols in their |
| // own code. |
| libraries = libraries.concat(JS_LIBRARIES); |
| |
| // Deduplicate libraries to avoid processing any library file multiple times |
| libraries = libraries.filter((item, pos) => libraries.indexOf(item) == pos); |
| |
| // Save the list for has() queries later. |
| this.libraries = libraries; |
| |
| for (const filename of libraries) { |
| const isUserLibrary = nodePath.isAbsolute(filename); |
| if (VERBOSE) { |
| if (isUserLibrary) { |
| printErr('processing user library: ' + filename); |
| } else { |
| printErr('processing system library: ' + filename); |
| } |
| } |
| let origLibrary = undefined; |
| let processed = undefined; |
| // When we parse user libraries also set `__user` attribute |
| // on each element so that we can distinguish them later. |
| if (isUserLibrary) { |
| origLibrary = this.library; |
| this.library = new Proxy(this.library, { |
| set: (target, prop, value) => { |
| target[prop] = value; |
| if (!isJsLibraryConfigIdentifier(prop)) { |
| target[prop + '__user'] = true; |
| } |
| return true; |
| }, |
| }); |
| } |
| currentFile = filename; |
| try { |
| processed = processMacros(preprocess(filename)); |
| vm.runInThisContext(processed, { filename: filename.replace(/\.\w+$/, '.preprocessed$&') }); |
| } catch (e) { |
| error(`failure to execute js library "${filename}":`); |
| if (VERBOSE) { |
| const orig = read(filename); |
| if (processed) { |
| error(`preprocessed source (you can run a js engine on this to get a clearer error message sometimes):\n=============\n${processed}\n=============`); |
| } else { |
| error(`original source:\n=============\n${orig}\n=============`); |
| } |
| } else { |
| error('use -sVERBOSE to see more details'); |
| } |
| throw e; |
| } finally { |
| currentFile = null; |
| if (origLibrary) { |
| this.library = origLibrary; |
| } |
| } |
| } |
| }, |
| }; |
| |
| if (!BOOTSTRAPPING_STRUCT_INFO) { |
| let structInfoFile = 'generated_struct_info32.json'; |
| if (MEMORY64) { |
| structInfoFile = 'generated_struct_info64.json' |
| } |
| // Load struct and define information. |
| const temp = JSON.parse(read(structInfoFile)); |
| C_STRUCTS = temp.structs; |
| C_DEFINES = temp.defines; |
| } else { |
| C_STRUCTS = {}; |
| C_DEFINES = {}; |
| } |
| |
| // Use proxy objects for C_DEFINES and C_STRUCTS so that we can give useful |
| // error messages. |
| C_STRUCTS = new Proxy(C_STRUCTS, { |
| get(target, prop, receiver) { |
| if (!(prop in target)) { |
| throw new Error(`Missing C struct ${prop}! If you just added it to struct_info.json, you need to run ./tools/gen_struct_info.py`); |
| } |
| return target[prop] |
| } |
| }); |
| |
| cDefs = C_DEFINES = new Proxy(C_DEFINES, { |
| get(target, prop, receiver) { |
| if (!(prop in target)) { |
| throw new Error(`Missing C define ${prop}! If you just added it to struct_info.json, you need to run ./tools/gen_struct_info.py`); |
| } |
| return target[prop] |
| } |
| }); |
| |
| // Legacy function that existed solely to give error message. These are now |
| // provided by the cDefs proxy object above. |
| function cDefine(key) { |
| return cDefs[key]; |
| } |
| |
| function isFSPrefixed(name) { |
| return name.length > 3 && name[0] === 'F' && name[1] === 'S' && name[2] === '_'; |
| } |
| |
| function isInternalSymbol(ident) { |
| return ident + '__internal' in LibraryManager.library; |
| } |
| |
| function getUnusedLibarySymbols() { |
| const librarySymbolSet = new Set(librarySymbols); |
| const missingSyms = new Set(); |
| for (const [ident, value] of Object.entries(LibraryManager.library)) { |
| if (typeof value === 'function' || typeof value === 'number') { |
| if (ident[0] === '$' && !isJsLibraryConfigIdentifier(ident) && !isInternalSymbol(ident)) { |
| const name = ident.substr(1); |
| if (!librarySymbolSet.has(name)) { |
| missingSyms.add(name); |
| } |
| } |
| } |
| } |
| return missingSyms; |
| } |
| |
| // When running with ASSERTIONS enabled we create stubs for each library |
| // function that that was not included in the build. This gives useful errors |
| // when library dependencies are missing from `__deps` or depended on without |
| // being added to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE |
| // TODO(sbc): These errors could potentially be generated at build time via |
| // some kind of acorn pass that searched for uses of these missing symbols. |
| function addMissingLibraryStubs(unusedLibSymbols) { |
| let rtn = ''; |
| rtn += 'var missingLibrarySymbols = [\n'; |
| for (const sym of unusedLibSymbols) { |
| rtn += ` '${sym}',\n`; |
| } |
| rtn += '];\n'; |
| rtn += 'missingLibrarySymbols.forEach(missingLibrarySymbol)\n'; |
| return rtn; |
| } |
| |
| // export parts of the JS runtime that the user asked for |
| function exportRuntime() { |
| const EXPORTED_RUNTIME_METHODS_SET = new Set(EXPORTED_RUNTIME_METHODS); |
| |
| const legacyRuntimeElements = new Map([ |
| ['print', 'out'], |
| ['printErr', 'err'], |
| ]); |
| |
| // optionally export something. |
| // in ASSERTIONS mode we show a useful error if it is used without |
| // being exported. how we show the message depends on whether it's |
| // a function (almost all of them) or a number. |
| function maybeExport(name) { |
| // if requested to be exported, export it |
| if (EXPORTED_RUNTIME_METHODS_SET.has(name)) { |
| let exported = name; |
| // the exported name may differ from the internal name |
| if (isFSPrefixed(exported)) { |
| // this is a filesystem value, FS.x exported as FS_x |
| exported = 'FS.' + exported.substr(3); |
| } else if (legacyRuntimeElements.has(exported)) { |
| exported = legacyRuntimeElements.get(exported); |
| } |
| return `Module["${name}"] = ${exported};`; |
| } |
| } |
| |
| // All possible runtime elements that can be exported |
| let runtimeElements = [ |
| 'run', |
| 'addOnPreRun', |
| 'addOnInit', |
| 'addOnPreMain', |
| 'addOnExit', |
| 'addOnPostRun', |
| 'addRunDependency', |
| 'removeRunDependency', |
| 'FS_createFolder', |
| 'FS_createPath', |
| 'FS_createDataFile', |
| 'FS_createPreloadedFile', |
| 'FS_createLazyFile', |
| 'FS_createLink', |
| 'FS_createDevice', |
| 'FS_unlink', |
| 'out', |
| 'err', |
| 'callMain', |
| 'abort', |
| 'keepRuntimeAlive', |
| 'wasmMemory', |
| ]; |
| |
| // These are actually native wasm functions these days but we allow exporting |
| // them via EXPORTED_RUNTIME_METHODS for backwards compat. |
| runtimeElements = runtimeElements.concat(WASM_SYSTEM_EXPORTS); |
| |
| if (PTHREADS && ALLOW_MEMORY_GROWTH) { |
| runtimeElements = runtimeElements.concat([ |
| 'GROWABLE_HEAP_I8', |
| 'GROWABLE_HEAP_U8', |
| 'GROWABLE_HEAP_I16', |
| 'GROWABLE_HEAP_U16', |
| 'GROWABLE_HEAP_I32', |
| 'GROWABLE_HEAP_U32', |
| 'GROWABLE_HEAP_F32', |
| 'GROWABLE_HEAP_F64', |
| ]); |
| } |
| if (USE_OFFSET_CONVERTER) { |
| runtimeElements.push('WasmOffsetConverter'); |
| } |
| |
| if (LOAD_SOURCE_MAP) { |
| runtimeElements.push('WasmSourceMap'); |
| } |
| |
| if (STACK_OVERFLOW_CHECK) { |
| runtimeElements.push('writeStackCookie'); |
| runtimeElements.push('checkStackCookie'); |
| } |
| |
| if (SUPPORT_BASE64_EMBEDDING) { |
| runtimeElements.push('intArrayFromBase64'); |
| runtimeElements.push('tryParseAsDataURI'); |
| } |
| |
| if (RETAIN_COMPILER_SETTINGS) { |
| runtimeElements.push('getCompilerSetting') |
| } |
| |
| if (RUNTIME_DEBUG) { |
| runtimeElements.push('prettyPrint') |
| } |
| |
| // dynCall_* methods are not hardcoded here, as they |
| // depend on the file being compiled. check for them |
| // and add them. |
| for (const name of EXPORTED_RUNTIME_METHODS_SET) { |
| if (/^dynCall_/.test(name)) { |
| // a specific dynCall; add to the list |
| runtimeElements.push(name); |
| } |
| } |
| |
| // Only export legacy runtime elements when explicitly |
| // requested. |
| for (const name of EXPORTED_RUNTIME_METHODS_SET) { |
| if (legacyRuntimeElements.has(name)) { |
| const newName = legacyRuntimeElements.get(name); |
| warn(`deprecated item in EXPORTED_RUNTIME_METHODS: ${name} use ${newName} instead.`); |
| runtimeElements.push(name); |
| } |
| } |
| |
| // Add JS library elements such as FS, GL, ENV, etc. These are prefixed with |
| // '$ which indicates they are JS methods. |
| let runtimeElementsSet = new Set(runtimeElements); |
| for (const ident of Object.keys(LibraryManager.library)) { |
| if (ident[0] === '$' && !isJsLibraryConfigIdentifier(ident) && !isInternalSymbol(ident)) { |
| const jsname = ident.substr(1); |
| assert(!runtimeElementsSet.has(jsname), 'runtimeElements contains library symbol: ' + ident); |
| runtimeElements.push(jsname); |
| } |
| } |
| |
| // check all exported things exist, warn about typos |
| runtimeElementsSet = new Set(runtimeElements); |
| for (const name of EXPORTED_RUNTIME_METHODS_SET) { |
| if (!runtimeElementsSet.has(name)) { |
| warn(`invalid item in EXPORTED_RUNTIME_METHODS: ${name}`); |
| } |
| } |
| |
| const exports = runtimeElements.map(maybeExport); |
| const results = exports.filter((name) => name); |
| |
| if (ASSERTIONS && !EXPORT_ALL) { |
| const unusedLibSymbols = getUnusedLibarySymbols(); |
| if (unusedLibSymbols.size) { |
| results.push(addMissingLibraryStubs(unusedLibSymbols)); |
| } |
| |
| const unexported = []; |
| for (const name of runtimeElements) { |
| if (!EXPORTED_RUNTIME_METHODS_SET.has(name) && !unusedLibSymbols.has(name)) { |
| unexported.push(name); |
| } |
| } |
| |
| if (unexported.length || unusedLibSymbols.size) { |
| let unexportedStubs = 'var unexportedSymbols = [\n'; |
| for (const sym of unexported) { |
| unexportedStubs += ` '${sym}',\n`; |
| } |
| unexportedStubs += '];\n'; |
| unexportedStubs += 'unexportedSymbols.forEach(unexportedRuntimeSymbol);\n'; |
| results.push(unexportedStubs); |
| } |
| } |
| |
| return results.join('\n') + '\n'; |
| } |