import * as path from 'node:path';
import {fileURLToPath} from 'node:url';
import assert from 'node:assert';
import {
isDecorator,
isJsOnlySymbol,
error,
readFile,
pushCurrentFile,
popCurrentFile,
printErr,
addToCompileTimeContext,
runInMacroContext,
mergeInto,
localFile,
} from './utility.mjs';
import {preprocess, processMacros} from './parseTools.mjs';
export const librarySymbols = [];
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() {
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 (EMSCRIPTEN_TRACING) {
libraries.push('libmemoryprofiler.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 (RELOCATABLE) {
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 {
libraries.push(
'libfs.js',
'libmemfs.js',
'libtty.js',
'libpipefs.js',
'libsockfs.js',
);
if (NODERAWFS) {
if (!JS_LIBRARIES.includes('libnodefs.js')) {
libraries.push('libnodefs.js');
}
libraries.push('libnoderawfs.js');
libraries.push('libnodepath.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) {
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 (USE_WEBGPU) {
libraries.push('libwebgpu.js');
libraries.push('libhtml5_webgpu.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');
}
libraries = libraries.map((filename) => path.join(systemLibdir, filename));
libraries.push(...JS_LIBRARIES);
libraries = libraries.filter((item, pos) => libraries.indexOf(item) == pos);
return libraries;
}
export const LibraryManager = {
library: {},
libraryDefinitions: {},
structs: {},
loaded: false,
libraries: [],
has(name) {
if (!path.isAbsolute(name)) {
if (name.startsWith('library_')) {
name = name.replace('library_', 'lib');
}
name = path.join(systemLibdir, name);
}
return this.libraries.includes(name);
},
load() {
assert(!this.loaded);
this.loaded = true;
this.libraries = calculateLibraries();
const userLibraryProxy = new Proxy(this.library, {
set(target, prop, value) {
target[prop] = value;
if (!isDecorator(prop)) {
target[prop + '__user'] = true;
}
return true;
},
});
for (let filename of this.libraries) {
const isUserLibrary = !isBeneath(filename, systemLibdir);
if (VERBOSE) {
if (isUserLibrary) {
printErr('processing user library: ' + filename);
} else {
printErr('processing system library: ' + filename);
}
}
let origLibrary = undefined;
let processed = undefined;
if (isUserLibrary) {
origLibrary = this.library;
this.library = userLibraryProxy;
}
pushCurrentFile(filename);
try {
processed = processMacros(preprocess(filename), filename);
runInMacroContext(processed, {filename: filename.replace(/\.\w+$/, '.preprocessed$&')});
} catch (e) {
error(`failure to execute js library "${filename}":`);
if (VERBOSE) {
const orig = readFile(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 {
popCurrentFile();
if (origLibrary) {
this.library = origLibrary;
}
}
}
},
};
function addToLibrary(obj, options = null) {
mergeInto(LibraryManager.library, obj, options);
}
let structs = {};
let defines = {};
function loadStructInfo(filename) {
const temp = JSON.parse(readFile(filename));
Object.assign(structs, temp.structs);
Object.assign(defines, temp.defines);
}
if (!BOOTSTRAPPING_STRUCT_INFO) {
if (MEMORY64) {
loadStructInfo(localFile('struct_info_generated_wasm64.json'));
} else {
loadStructInfo(localFile('struct_info_generated.json'));
}
}
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];
},
});
const cDefs = C_DEFINES;
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;
}
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) {
if (MODULARIZE === 'instance') {
return name;
}
return `Module['${name}'] = ${name};`;
}
function exportRuntimeSymbols() {
function shouldExport(name) {
if (EXPORTED_RUNTIME_METHODS.has(name)) {
if (MODULARIZE == 'instance' || !name.startsWith('HEAP')) {
return true;
}
}
return false;
}
let runtimeElements = [
'run',
'out',
'err',
'callMain',
'abort',
'wasmMemory',
'wasmExports',
'HEAPF32',
'HEAPF64',
'HEAP8',
'HEAPU8',
'HEAP16',
'HEAPU16',
'HEAP32',
'HEAPU32',
'HEAP64',
'HEAPU64',
];
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');
}
for (const name of EXPORTED_RUNTIME_METHODS) {
if (/^dynCall_/.test(name)) {
runtimeElements.push(name);
}
}
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);
assert(!runtimeElementsSet.has(jsname), 'runtimeElements contains library symbol: ' + ident);
runtimeElements.push(jsname);
}
}
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) {
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)) {
results.push(exportSymbol(ident));
}
}
results.push('// End JS library exports');
return results.join('\n ') + '\n';
}
function exportJSSymbols() {
if (MODULARIZE == 'instance') return exportRuntimeSymbols();
return exportRuntimeSymbols() + ' ' + exportLibrarySymbols();
}
addToCompileTimeContext({
exportJSSymbols,
loadStructInfo,
LibraryManager,
librarySymbols,
addToLibrary,
cDefs,
cDefine,
C_STRUCTS,
C_DEFINES,
});