Buckets:
| /** | |
| * @license | |
| * Copyright 2011 The Emscripten Authors | |
| * SPDX-License-Identifier: MIT | |
| */ | |
| import * as os from 'node:os'; | |
| import * as fs from 'node:fs'; | |
| import * as path from 'node:path'; | |
| import {fileURLToPath} from 'node:url'; | |
| import assert from 'node:assert'; | |
| import { | |
| debugLog, | |
| isDecorator, | |
| isJsOnlySymbol, | |
| error, | |
| readFile, | |
| pushCurrentFile, | |
| popCurrentFile, | |
| addToCompileTimeContext, | |
| runInMacroContext, | |
| mergeInto, | |
| localFile, | |
| timer, | |
| } from './utility.mjs'; | |
| import {preprocess, processMacros} from './parseTools.mjs'; | |
| // Various namespace-like modules | |
| // List of symbols that were added from the library. | |
| export const librarySymbols = []; | |
| // Map of library symbols which are aliases for native symbols | |
| // e.g. `wasmTable` -> `__indirect_function_table` | |
| export const nativeAliases = {}; | |
| const srcDir = fileURLToPath(new URL('.', import.meta.url)); | |
| const systemLibdir = path.join(srcDir, 'lib'); | |
| function isBeneath(childPath, parentPath) { | |
| const relativePath = path.relative(parentPath, childPath); | |
| return !relativePath.startsWith('..') && !path.isAbsolute(relativePath); | |
| } | |
| function calculateLibraries() { | |
| // Core system libraries (always linked against) | |
| let libraries = [ | |
| 'libint53.js', | |
| 'libcore.js', | |
| 'libsigs.js', | |
| 'libccall.js', | |
| 'libaddfunction.js', | |
| 'libgetvalue.js', | |
| 'libmath.js', | |
| 'libpath.js', | |
| 'libstrings.js', | |
| 'libhtml5.js', | |
| 'libstack_trace.js', | |
| 'libwasi.js', | |
| 'libeventloop.js', | |
| 'libpromise.js', | |
| ]; | |
| if (LINK_AS_CXX) { | |
| if (DISABLE_EXCEPTION_THROWING && !WASM_EXCEPTIONS) { | |
| libraries.push('libexceptions_stub.js'); | |
| } else { | |
| libraries.push('libexceptions.js'); | |
| } | |
| } | |
| if (!MINIMAL_RUNTIME) { | |
| libraries.push('libbrowser.js'); | |
| libraries.push('libwget.js'); | |
| } | |
| if (!STANDALONE_WASM) { | |
| libraries.push('libtime.js'); | |
| } | |
| if (SUPPORT_BASE64_EMBEDDING || ENVIRONMENT_MAY_BE_SHELL) { | |
| libraries.push('libbase64.js'); | |
| } | |
| if (AUTODEBUG) { | |
| libraries.push('libautodebug.js'); | |
| } | |
| if (!WASMFS) { | |
| libraries.push('libsyscall.js'); | |
| } | |
| if (MAIN_MODULE) { | |
| libraries.push('libdylink.js'); | |
| } | |
| if (FILESYSTEM) { | |
| libraries.push('libfs_shared.js'); | |
| if (WASMFS) { | |
| libraries.push( | |
| 'libwasmfs.js', | |
| 'libwasmfs_js_file.js', | |
| 'libwasmfs_jsimpl.js', | |
| 'libwasmfs_fetch.js', | |
| 'libwasmfs_node.js', | |
| 'libwasmfs_opfs.js', | |
| ); | |
| } else { | |
| // Core filesystem libraries (always linked against, unless -sFILESYSTEM=0 is specified) | |
| libraries.push( | |
| 'libfs.js', | |
| 'libmemfs.js', | |
| 'libtty.js', | |
| 'libpipefs.js', // ok to include it by default since it's only used if the syscall is used | |
| 'libsockfs.js', // ok to include it by default since it's only used if the syscall is used | |
| ); | |
| if (NODERAWFS) { | |
| // NODERAWFS requires NODEFS | |
| libraries.push('libnodefs.js'); | |
| libraries.push('libnoderawfs.js'); | |
| // NODERAWFS overwrites libpath.js | |
| libraries.push('libnodepath.js'); | |
| } | |
| } | |
| } | |
| // Additional JS libraries (without AUTO_JS_LIBRARIES, link to these explicitly via -lxxx.js) | |
| if (AUTO_JS_LIBRARIES) { | |
| libraries.push( | |
| 'libwebgl.js', | |
| 'libhtml5_webgl.js', | |
| 'libopenal.js', | |
| 'libglut.js', | |
| 'libxlib.js', | |
| 'libegl.js', | |
| 'libuuid.js', | |
| 'libglew.js', | |
| 'libidbstore.js', | |
| 'libasync.js', | |
| ); | |
| if (USE_SDL != 2) { | |
| libraries.push('libsdl.js'); | |
| } | |
| } else { | |
| if (ASYNCIFY) { | |
| libraries.push('libasync.js'); | |
| } | |
| if (USE_SDL == 1) { | |
| libraries.push('libsdl.js'); | |
| } | |
| if (USE_SDL == 2) { | |
| libraries.push('libegl.js', 'libwebgl.js', 'libhtml5_webgl.js'); | |
| } | |
| } | |
| if (USE_GLFW) { | |
| libraries.push('libglfw.js'); | |
| } | |
| if (LZ4) { | |
| libraries.push('liblz4.js'); | |
| } | |
| if (SHARED_MEMORY) { | |
| libraries.push('libatomic.js'); | |
| } | |
| if (MAX_WEBGL_VERSION >= 2) { | |
| // libwebgl2.js must be included only after libwebgl.js, so if we are | |
| // about to include libwebgl2.js, first squeeze in libwebgl.js. | |
| libraries.push('libwebgl.js'); | |
| libraries.push('libwebgl2.js'); | |
| } | |
| if (GL_EXPLICIT_UNIFORM_LOCATION || GL_EXPLICIT_UNIFORM_BINDING) { | |
| libraries.push('libc_preprocessor.js'); | |
| } | |
| if (LEGACY_GL_EMULATION) { | |
| libraries.push('libglemu.js'); | |
| } | |
| if (!STRICT) { | |
| libraries.push('liblegacy.js'); | |
| } | |
| if (BOOTSTRAPPING_STRUCT_INFO) { | |
| libraries = ['libbootstrap.js', 'libstrings.js', 'libint53.js']; | |
| } | |
| if (SUPPORT_BIG_ENDIAN) { | |
| libraries.push('liblittle_endian_heap.js'); | |
| } | |
| // Resolve system libraries | |
| libraries = libraries.map((filename) => path.join(systemLibdir, filename)); | |
| // 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.push(...JS_LIBRARIES); | |
| // Deduplicate libraries to avoid processing any library file multiple times | |
| libraries = [...new Set(libraries)] | |
| return libraries; | |
| } | |
| let tempDir; | |
| function getTempDir() { | |
| if (!tempDir) { | |
| const tempRoot = os.tmpdir(); | |
| tempDir = fs.mkdtempSync(path.join(tempRoot, 'emcc-jscompiler-')); | |
| } | |
| return tempDir; | |
| } | |
| function preprocessFiles(filenames) { | |
| timer.start('preprocessFiles') | |
| const results = {}; | |
| for (const filename of filenames) { | |
| debugLog(`pre-processing JS library: ${filename}`); | |
| pushCurrentFile(filename); | |
| try { | |
| results[filename] = processMacros(preprocess(filename), filename); | |
| } catch (e) { | |
| error(`error preprocessing JS library "${filename}":`); | |
| throw e; | |
| } finally { | |
| popCurrentFile(); | |
| } | |
| } | |
| timer.stop('preprocessFiles') | |
| return results; | |
| } | |
| export const LibraryManager = { | |
| library: {}, | |
| // The JS and JS docs of each library definition indexed my mangled name. | |
| libraryDefinitions: {}, | |
| structs: {}, | |
| loaded: false, | |
| libraries: [], | |
| has(name) { | |
| if (!path.isAbsolute(name)) { | |
| // Our libraries used to be called `library_xxx.js` rather than | |
| // `lib_xx.js`. In case we have external code using this function | |
| // we check for the old form too. | |
| if (name.startsWith('library_')) { | |
| name = name.replace('library_', 'lib'); | |
| } | |
| name = path.join(systemLibdir, name); | |
| } | |
| return this.libraries.includes(name); | |
| }, | |
| load() { | |
| timer.start('load') | |
| assert(!this.loaded); | |
| this.loaded = true; | |
| // Save the list for has() queries later. | |
| this.libraries = calculateLibraries(); | |
| const preprocessed = preprocessFiles(this.libraries); | |
| timer.start('executeJS') | |
| for (const [filename, contents] of Object.entries(preprocessed)) { | |
| this.executeJSLibraryFile(filename, contents); | |
| } | |
| timer.stop('executeJS') | |
| this.addAliasDependencies(); | |
| timer.stop('load') | |
| }, | |
| isAlias(entry) { | |
| return (typeof entry == 'string' && entry[0] != '=' && (this.library.hasOwnProperty(entry) || WASM_EXPORTS.has(entry))); | |
| }, | |
| /** | |
| * Automatically add the target of an alias to it's dependency list. | |
| */ | |
| addAliasDependencies() { | |
| const aliases = {}; | |
| for (const [key, value] of Object.entries(this.library)) { | |
| if (this.isAlias(value)) { | |
| aliases[key] = value; | |
| } | |
| } | |
| for (const [key, value] of Object.entries(aliases)) { | |
| (this.library[key + '__deps'] ??= []).push(value); | |
| } | |
| }, | |
| executeJSLibraryFile(filename, contents) { | |
| const userLibraryProxy = new Proxy(this.library, { | |
| set(target, prop, value) { | |
| target[prop] = value; | |
| if (!isDecorator(prop)) { | |
| target[prop + '__user'] = true; | |
| } | |
| return true; | |
| }, | |
| }); | |
| const isUserLibrary = !isBeneath(filename, systemLibdir); | |
| if (isUserLibrary) { | |
| debugLog(`executing user JS library: ${filename}`); | |
| } else { | |
| debugLog(`exectuing system JS library: ${filename}`); | |
| } | |
| let origLibrary; | |
| // 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 = userLibraryProxy; | |
| } | |
| pushCurrentFile(filename); | |
| let preprocessedName = filename.replace(/\.\w+$/, '.preprocessed$&') | |
| if (VERBOSE) { | |
| preprocessedName = path.join(getTempDir(), path.basename(filename)); | |
| } | |
| try { | |
| runInMacroContext(contents, {filename: preprocessedName}) | |
| } catch (e) { | |
| error(`failure to execute JS library "${filename}":`); | |
| if (VERBOSE) { | |
| fs.writeFileSync(preprocessedName, contents); | |
| error(`preprocessed JS saved to ${preprocessedName}`) | |
| } else { | |
| error('use -sVERBOSE to save preprocessed JS'); | |
| } | |
| throw e; | |
| } finally { | |
| popCurrentFile(); | |
| if (origLibrary) { | |
| this.library = origLibrary; | |
| } | |
| } | |
| if (VERBOSE) { | |
| fs.rmSync(getTempDir(), { recursive: true, force: true }); | |
| } | |
| } | |
| }; | |
| // options is optional input object containing mergeInto params | |
| // currently, it can contain | |
| // | |
| // key: noOverride, value: true | |
| // if it is set, it prevents symbol redefinition and shows error | |
| // in case of redefinition | |
| // | |
| // key: checkSig, value: true | |
| // if it is set, __sig is checked for functions and error is reported | |
| // if <function name>__sig is missing | |
| function addToLibrary(obj, options = null) { | |
| mergeInto(LibraryManager.library, obj, options); | |
| } | |
| let structs = {}; | |
| let defines = {}; | |
| /** | |
| * Read JSON file containing struct and macro/define information | |
| * that can then be used in JavaScript via macros. | |
| */ | |
| function loadStructInfo(filename) { | |
| const temp = JSON.parse(readFile(filename)); | |
| Object.assign(structs, temp.structs); | |
| Object.assign(defines, temp.defines); | |
| } | |
| if (!BOOTSTRAPPING_STRUCT_INFO) { | |
| // Load struct and define information. | |
| if (MEMORY64) { | |
| loadStructInfo(localFile('struct_info_generated_wasm64.json')); | |
| } else { | |
| loadStructInfo(localFile('struct_info_generated.json')); | |
| } | |
| } | |
| // Use proxy objects for C_DEFINES and C_STRUCTS so that we can give useful | |
| // error messages. | |
| const C_STRUCTS = new Proxy(structs, { | |
| get(target, prop) { | |
| 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 (then run a second time with --wasm64)`, | |
| ); | |
| } | |
| return target[prop]; | |
| }, | |
| }); | |
| const C_DEFINES = new Proxy(defines, { | |
| get(target, prop) { | |
| 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 (then run a second time with --wasm64)`, | |
| ); | |
| } | |
| return target[prop]; | |
| }, | |
| }); | |
| // shorter alias for C_DEFINES | |
| const cDefs = C_DEFINES; | |
| // 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 isInternalSymbol(ident) { | |
| return ident + '__internal' in LibraryManager.library; | |
| } | |
| function getUnusedLibrarySymbols() { | |
| 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 (isJsOnlySymbol(ident) && !isDecorator(ident) && !isInternalSymbol(ident)) { | |
| const name = ident.slice(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; | |
| } | |
| function exportSymbol(name) { | |
| // In MODULARIZE=instance mode symbols are exported by being included in | |
| // an export { foo, bar } list so we build up the simple list of names | |
| if (MODULARIZE === 'instance') { | |
| return name; | |
| } | |
| return `Module['${name}'] = ${name};`; | |
| } | |
| // export parts of the JS runtime that the user asked for | |
| function exportRuntimeSymbols() { | |
| // optionally export something. | |
| function shouldExport(name) { | |
| // Native exports are not available to be exported initially. Instead, | |
| // they get exported later in `assignWasmExports`. | |
| if (nativeAliases[name]) { | |
| return false; | |
| } | |
| // If requested to be exported, export it. | |
| if (EXPORTED_RUNTIME_METHODS.has(name)) { | |
| // Unless we are in MODULARIZE=instance mode then HEAP objects are | |
| // exported separately in updateMemoryViews | |
| if (MODULARIZE == 'instance' || !name.startsWith('HEAP')) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // All possible runtime elements that can be exported | |
| let runtimeElements = [ | |
| 'run', | |
| 'out', | |
| 'err', | |
| 'callMain', | |
| 'abort', | |
| 'wasmExports', | |
| ]; | |
| if (SUPPORT_BIG_ENDIAN) { | |
| runtimeElements.push('HEAP_DATA_VIEW'); | |
| } | |
| if (LOAD_SOURCE_MAP) { | |
| runtimeElements.push('WasmSourceMap'); | |
| } | |
| if (STACK_OVERFLOW_CHECK) { | |
| runtimeElements.push('writeStackCookie'); | |
| runtimeElements.push('checkStackCookie'); | |
| } | |
| 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) { | |
| if (/^dynCall_/.test(name)) { | |
| // a specific dynCall; add to the list | |
| 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 (isJsOnlySymbol(ident) && !isDecorator(ident) && !isInternalSymbol(ident)) { | |
| const jsname = ident.slice(1); | |
| // Note that this assertion may be hit when a function is moved into the | |
| // JS library. In that case the function should be removed from the list | |
| // of runtime elements above. | |
| assert(!runtimeElementsSet.has(jsname), 'runtimeElements contains library symbol: ' + ident); | |
| runtimeElements.push(jsname); | |
| } | |
| } | |
| // check all exported things exist, error when missing | |
| runtimeElementsSet = new Set(runtimeElements); | |
| for (const name of EXPORTED_RUNTIME_METHODS) { | |
| if (!runtimeElementsSet.has(name)) { | |
| error(`undefined exported symbol: "${name}" in EXPORTED_RUNTIME_METHODS`); | |
| } | |
| } | |
| const exports = runtimeElements.filter(shouldExport); | |
| const results = exports.map(exportSymbol); | |
| if (MODULARIZE == 'instance') { | |
| if (results.length == 0) return ''; | |
| return '// Runtime exports\nexport { ' + results.join(', ') + ' };\n'; | |
| } | |
| if (ASSERTIONS && !EXPORT_ALL) { | |
| // in ASSERTIONS mode we show a useful error if it is used without being | |
| // exported. See `unexportedRuntimeSymbol` in runtime_debug.js. | |
| const unusedLibSymbols = getUnusedLibrarySymbols(); | |
| if (unusedLibSymbols.size) { | |
| results.push(addMissingLibraryStubs(unusedLibSymbols)); | |
| } | |
| const unexported = []; | |
| for (const name of runtimeElements) { | |
| if ( | |
| !EXPORTED_RUNTIME_METHODS.has(name) && | |
| !EXPORTED_FUNCTIONS.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); | |
| } | |
| } | |
| results.unshift('// Begin runtime exports'); | |
| results.push('// End runtime exports'); | |
| return results.join('\n ') + '\n'; | |
| } | |
| function exportLibrarySymbols() { | |
| assert(MODULARIZE != 'instance'); | |
| const results = ['// Begin JS library exports']; | |
| for (const ident of librarySymbols) { | |
| if ((EXPORT_ALL || EXPORTED_FUNCTIONS.has(ident)) && !nativeAliases[ident]) { | |
| results.push(exportSymbol(ident)); | |
| } | |
| } | |
| results.push('// End JS library exports'); | |
| return results.join('\n ') + '\n'; | |
| } | |
| function exportJSSymbols() { | |
| // In MODULARIZE=instance mode JS library symbols are marked with `export` | |
| // at the point of declaration. | |
| if (MODULARIZE == 'instance') return exportRuntimeSymbols(); | |
| return exportRuntimeSymbols() + ' ' + exportLibrarySymbols(); | |
| } | |
| addToCompileTimeContext({ | |
| exportJSSymbols, | |
| loadStructInfo, | |
| LibraryManager, | |
| librarySymbols, | |
| addToLibrary, | |
| cDefs, | |
| cDefine, | |
| C_STRUCTS, | |
| C_DEFINES, | |
| }); | |
Xet Storage Details
- Size:
- 17.4 kB
- Xet hash:
- c6574a717dd7a1cc674ae7e2c7395307bcecfbbf29e35280f623f0c3d2797342
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.