Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/modules.mjs
4128 views
1
/**
2
* @license
3
* Copyright 2011 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
import * as path from 'node:path';
8
import {fileURLToPath} from 'node:url';
9
import assert from 'node:assert';
10
11
import {
12
isDecorator,
13
isJsOnlySymbol,
14
error,
15
readFile,
16
pushCurrentFile,
17
popCurrentFile,
18
printErr,
19
addToCompileTimeContext,
20
runInMacroContext,
21
mergeInto,
22
localFile,
23
} from './utility.mjs';
24
import {preprocess, processMacros} from './parseTools.mjs';
25
26
// Various namespace-like modules
27
28
// List of symbols that were added from the library.
29
export const librarySymbols = [];
30
31
const srcDir = fileURLToPath(new URL('.', import.meta.url));
32
const systemLibdir = path.join(srcDir, 'lib');
33
34
function isBeneath(childPath, parentPath) {
35
const relativePath = path.relative(parentPath, childPath);
36
return !relativePath.startsWith('..') && !path.isAbsolute(relativePath);
37
}
38
39
function calculateLibraries() {
40
// Core system libraries (always linked against)
41
let libraries = [
42
'libint53.js',
43
'libcore.js',
44
'libsigs.js',
45
'libccall.js',
46
'libaddfunction.js',
47
'libgetvalue.js',
48
'libmath.js',
49
'libpath.js',
50
'libstrings.js',
51
'libhtml5.js',
52
'libstack_trace.js',
53
'libwasi.js',
54
'libeventloop.js',
55
'libpromise.js',
56
];
57
58
if (LINK_AS_CXX) {
59
if (DISABLE_EXCEPTION_THROWING && !WASM_EXCEPTIONS) {
60
libraries.push('libexceptions_stub.js');
61
} else {
62
libraries.push('libexceptions.js');
63
}
64
}
65
66
if (!MINIMAL_RUNTIME) {
67
libraries.push('libbrowser.js');
68
libraries.push('libwget.js');
69
}
70
71
if (!STANDALONE_WASM) {
72
libraries.push('libtime.js');
73
}
74
75
if (EMSCRIPTEN_TRACING) {
76
libraries.push('libmemoryprofiler.js');
77
}
78
79
if (SUPPORT_BASE64_EMBEDDING || ENVIRONMENT_MAY_BE_SHELL) {
80
libraries.push('libbase64.js');
81
}
82
83
if (AUTODEBUG) {
84
libraries.push('libautodebug.js');
85
}
86
87
if (!WASMFS) {
88
libraries.push('libsyscall.js');
89
}
90
91
if (RELOCATABLE) {
92
libraries.push('libdylink.js');
93
}
94
95
if (FILESYSTEM) {
96
libraries.push('libfs_shared.js');
97
if (WASMFS) {
98
libraries.push(
99
'libwasmfs.js',
100
'libwasmfs_js_file.js',
101
'libwasmfs_jsimpl.js',
102
'libwasmfs_fetch.js',
103
'libwasmfs_node.js',
104
'libwasmfs_opfs.js',
105
);
106
} else {
107
// Core filesystem libraries (always linked against, unless -sFILESYSTEM=0 is specified)
108
libraries.push(
109
'libfs.js',
110
'libmemfs.js',
111
'libtty.js',
112
'libpipefs.js', // ok to include it by default since it's only used if the syscall is used
113
'libsockfs.js', // ok to include it by default since it's only used if the syscall is used
114
);
115
116
if (NODERAWFS) {
117
// NODERAWFS requires NODEFS
118
if (!JS_LIBRARIES.includes('libnodefs.js')) {
119
libraries.push('libnodefs.js');
120
}
121
libraries.push('libnoderawfs.js');
122
// NODERAWFS overwrites libpath.js
123
libraries.push('libnodepath.js');
124
}
125
}
126
}
127
128
// Additional JS libraries (without AUTO_JS_LIBRARIES, link to these explicitly via -lxxx.js)
129
if (AUTO_JS_LIBRARIES) {
130
libraries.push(
131
'libwebgl.js',
132
'libhtml5_webgl.js',
133
'libopenal.js',
134
'libglut.js',
135
'libxlib.js',
136
'libegl.js',
137
'libuuid.js',
138
'libglew.js',
139
'libidbstore.js',
140
'libasync.js',
141
);
142
if (USE_SDL != 2) {
143
libraries.push('libsdl.js');
144
}
145
} else {
146
if (ASYNCIFY) {
147
libraries.push('libasync.js');
148
}
149
if (USE_SDL == 1) {
150
libraries.push('libsdl.js');
151
}
152
if (USE_SDL == 2) {
153
libraries.push('libegl.js', 'libwebgl.js', 'libhtml5_webgl.js');
154
}
155
}
156
157
if (USE_GLFW) {
158
libraries.push('libglfw.js');
159
}
160
161
if (LZ4) {
162
libraries.push('liblz4.js');
163
}
164
165
if (SHARED_MEMORY) {
166
libraries.push('libatomic.js');
167
}
168
169
if (MAX_WEBGL_VERSION >= 2) {
170
// libwebgl2.js must be included only after libwebgl.js, so if we are
171
// about to include libwebgl2.js, first squeeze in libwebgl.js.
172
libraries.push('libwebgl.js');
173
libraries.push('libwebgl2.js');
174
}
175
176
if (GL_EXPLICIT_UNIFORM_LOCATION || GL_EXPLICIT_UNIFORM_BINDING) {
177
libraries.push('libc_preprocessor.js');
178
}
179
180
if (LEGACY_GL_EMULATION) {
181
libraries.push('libglemu.js');
182
}
183
184
if (USE_WEBGPU) {
185
libraries.push('libwebgpu.js');
186
libraries.push('libhtml5_webgpu.js');
187
}
188
189
if (!STRICT) {
190
libraries.push('liblegacy.js');
191
}
192
193
if (BOOTSTRAPPING_STRUCT_INFO) {
194
libraries = ['libbootstrap.js', 'libstrings.js', 'libint53.js'];
195
}
196
197
if (SUPPORT_BIG_ENDIAN) {
198
libraries.push('liblittle_endian_heap.js');
199
}
200
201
// Resolve system libraries
202
libraries = libraries.map((filename) => path.join(systemLibdir, filename));
203
204
// Add all user specified JS library files to the link.
205
// These must be added last after all Emscripten-provided system libraries
206
// above, so that users can override built-in JS library symbols in their
207
// own code.
208
libraries.push(...JS_LIBRARIES);
209
210
// Deduplicate libraries to avoid processing any library file multiple times
211
libraries = libraries.filter((item, pos) => libraries.indexOf(item) == pos);
212
213
return libraries;
214
}
215
216
export const LibraryManager = {
217
library: {},
218
// The JS and JS docs of each library definition indexed my mangled name.
219
libraryDefinitions: {},
220
structs: {},
221
loaded: false,
222
libraries: [],
223
224
has(name) {
225
if (!path.isAbsolute(name)) {
226
// Our libraries used to be called `library_xxx.js` rather than
227
// `lib_xx.js`. In case we have external code using this function
228
// we check for the old form too.
229
if (name.startsWith('library_')) {
230
name = name.replace('library_', 'lib');
231
}
232
name = path.join(systemLibdir, name);
233
}
234
return this.libraries.includes(name);
235
},
236
237
load() {
238
assert(!this.loaded);
239
this.loaded = true;
240
// Save the list for has() queries later.
241
this.libraries = calculateLibraries();
242
243
const userLibraryProxy = new Proxy(this.library, {
244
set(target, prop, value) {
245
target[prop] = value;
246
if (!isDecorator(prop)) {
247
target[prop + '__user'] = true;
248
}
249
return true;
250
},
251
});
252
253
for (let filename of this.libraries) {
254
const isUserLibrary = !isBeneath(filename, systemLibdir);
255
256
if (VERBOSE) {
257
if (isUserLibrary) {
258
printErr('processing user library: ' + filename);
259
} else {
260
printErr('processing system library: ' + filename);
261
}
262
}
263
let origLibrary = undefined;
264
let processed = undefined;
265
// When we parse user libraries also set `__user` attribute
266
// on each element so that we can distinguish them later.
267
if (isUserLibrary) {
268
origLibrary = this.library;
269
this.library = userLibraryProxy;
270
}
271
pushCurrentFile(filename);
272
try {
273
processed = processMacros(preprocess(filename), filename);
274
runInMacroContext(processed, {filename: filename.replace(/\.\w+$/, '.preprocessed$&')});
275
} catch (e) {
276
error(`failure to execute js library "${filename}":`);
277
if (VERBOSE) {
278
const orig = readFile(filename);
279
if (processed) {
280
error(
281
`preprocessed source (you can run a js engine on this to get a clearer error message sometimes):\n=============\n${processed}\n=============`,
282
);
283
} else {
284
error(`original source:\n=============\n${orig}\n=============`);
285
}
286
} else {
287
error('use -sVERBOSE to see more details');
288
}
289
throw e;
290
} finally {
291
popCurrentFile();
292
if (origLibrary) {
293
this.library = origLibrary;
294
}
295
}
296
}
297
},
298
};
299
300
// options is optional input object containing mergeInto params
301
// currently, it can contain
302
//
303
// key: noOverride, value: true
304
// if it is set, it prevents symbol redefinition and shows error
305
// in case of redefinition
306
//
307
// key: checkSig, value: true
308
// if it is set, __sig is checked for functions and error is reported
309
// if <function name>__sig is missing
310
function addToLibrary(obj, options = null) {
311
mergeInto(LibraryManager.library, obj, options);
312
}
313
314
let structs = {};
315
let defines = {};
316
317
/**
318
* Read JSON file containing struct and macro/define information
319
* that can then be used in JavaScript via macros.
320
*/
321
function loadStructInfo(filename) {
322
const temp = JSON.parse(readFile(filename));
323
Object.assign(structs, temp.structs);
324
Object.assign(defines, temp.defines);
325
}
326
327
if (!BOOTSTRAPPING_STRUCT_INFO) {
328
// Load struct and define information.
329
if (MEMORY64) {
330
loadStructInfo(localFile('struct_info_generated_wasm64.json'));
331
} else {
332
loadStructInfo(localFile('struct_info_generated.json'));
333
}
334
}
335
336
// Use proxy objects for C_DEFINES and C_STRUCTS so that we can give useful
337
// error messages.
338
const C_STRUCTS = new Proxy(structs, {
339
get(target, prop) {
340
if (!(prop in target)) {
341
throw new Error(
342
`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)`,
343
);
344
}
345
return target[prop];
346
},
347
});
348
349
const C_DEFINES = new Proxy(defines, {
350
get(target, prop) {
351
if (!(prop in target)) {
352
throw new Error(
353
`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)`,
354
);
355
}
356
return target[prop];
357
},
358
});
359
360
// shorter alias for C_DEFINES
361
const cDefs = C_DEFINES;
362
363
// Legacy function that existed solely to give error message. These are now
364
// provided by the cDefs proxy object above.
365
function cDefine(key) {
366
return cDefs[key];
367
}
368
369
function isInternalSymbol(ident) {
370
return ident + '__internal' in LibraryManager.library;
371
}
372
373
function getUnusedLibrarySymbols() {
374
const librarySymbolSet = new Set(librarySymbols);
375
const missingSyms = new Set();
376
for (const [ident, value] of Object.entries(LibraryManager.library)) {
377
if (typeof value === 'function' || typeof value === 'number') {
378
if (isJsOnlySymbol(ident) && !isDecorator(ident) && !isInternalSymbol(ident)) {
379
const name = ident.slice(1);
380
if (!librarySymbolSet.has(name)) {
381
missingSyms.add(name);
382
}
383
}
384
}
385
}
386
return missingSyms;
387
}
388
389
// When running with ASSERTIONS enabled we create stubs for each library
390
// function that that was not included in the build. This gives useful errors
391
// when library dependencies are missing from `__deps` or depended on without
392
// being added to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE
393
// TODO(sbc): These errors could potentially be generated at build time via
394
// some kind of acorn pass that searched for uses of these missing symbols.
395
function addMissingLibraryStubs(unusedLibSymbols) {
396
let rtn = '';
397
rtn += 'var missingLibrarySymbols = [\n';
398
for (const sym of unusedLibSymbols) {
399
rtn += ` '${sym}',\n`;
400
}
401
rtn += '];\n';
402
rtn += 'missingLibrarySymbols.forEach(missingLibrarySymbol)\n';
403
return rtn;
404
}
405
406
function exportSymbol(name) {
407
// In MODULARIZE=instance mode symbols are exported by being included in
408
// an export { foo, bar } list so we build up the simple list of names
409
if (MODULARIZE === 'instance') {
410
return name;
411
}
412
return `Module['${name}'] = ${name};`;
413
}
414
415
// export parts of the JS runtime that the user asked for
416
function exportRuntimeSymbols() {
417
// optionally export something.
418
function shouldExport(name) {
419
// If requested to be exported, export it.
420
if (EXPORTED_RUNTIME_METHODS.has(name)) {
421
// Unless we are in MODULARIZE=instance mode then HEAP objects are
422
// exported separately in updateMemoryViews
423
if (MODULARIZE == 'instance' || !name.startsWith('HEAP')) {
424
return true;
425
}
426
}
427
return false;
428
}
429
430
// All possible runtime elements that can be exported
431
let runtimeElements = [
432
'run',
433
'out',
434
'err',
435
'callMain',
436
'abort',
437
'wasmMemory',
438
'wasmExports',
439
'HEAPF32',
440
'HEAPF64',
441
'HEAP8',
442
'HEAPU8',
443
'HEAP16',
444
'HEAPU16',
445
'HEAP32',
446
'HEAPU32',
447
'HEAP64',
448
'HEAPU64',
449
];
450
451
if (SUPPORT_BIG_ENDIAN) {
452
runtimeElements.push('HEAP_DATA_VIEW');
453
}
454
455
if (LOAD_SOURCE_MAP) {
456
runtimeElements.push('WasmSourceMap');
457
}
458
459
if (STACK_OVERFLOW_CHECK) {
460
runtimeElements.push('writeStackCookie');
461
runtimeElements.push('checkStackCookie');
462
}
463
464
if (RETAIN_COMPILER_SETTINGS) {
465
runtimeElements.push('getCompilerSetting');
466
}
467
468
if (RUNTIME_DEBUG) {
469
runtimeElements.push('prettyPrint');
470
}
471
472
// dynCall_* methods are not hardcoded here, as they
473
// depend on the file being compiled. check for them
474
// and add them.
475
for (const name of EXPORTED_RUNTIME_METHODS) {
476
if (/^dynCall_/.test(name)) {
477
// a specific dynCall; add to the list
478
runtimeElements.push(name);
479
}
480
}
481
482
// Add JS library elements such as FS, GL, ENV, etc. These are prefixed with
483
// '$ which indicates they are JS methods.
484
let runtimeElementsSet = new Set(runtimeElements);
485
for (const ident of Object.keys(LibraryManager.library)) {
486
if (isJsOnlySymbol(ident) && !isDecorator(ident) && !isInternalSymbol(ident)) {
487
const jsname = ident.slice(1);
488
// Note that this assertion may be hit when a function is moved into the
489
// JS library. In that case the function should be removed from the list
490
// of runtime elements above.
491
assert(!runtimeElementsSet.has(jsname), 'runtimeElements contains library symbol: ' + ident);
492
runtimeElements.push(jsname);
493
}
494
}
495
496
// check all exported things exist, error when missing
497
runtimeElementsSet = new Set(runtimeElements);
498
for (const name of EXPORTED_RUNTIME_METHODS) {
499
if (!runtimeElementsSet.has(name)) {
500
error(`undefined exported symbol: "${name}" in EXPORTED_RUNTIME_METHODS`);
501
}
502
}
503
504
const exports = runtimeElements.filter(shouldExport);
505
const results = exports.map(exportSymbol);
506
507
if (MODULARIZE == 'instance') {
508
if (results.length == 0) return '';
509
return '// Runtime exports\nexport { ' + results.join(', ') + ' };\n';
510
}
511
512
if (ASSERTIONS && !EXPORT_ALL) {
513
// in ASSERTIONS mode we show a useful error if it is used without being
514
// exported. See `unexportedRuntimeSymbol` in runtime_debug.js.
515
const unusedLibSymbols = getUnusedLibrarySymbols();
516
if (unusedLibSymbols.size) {
517
results.push(addMissingLibraryStubs(unusedLibSymbols));
518
}
519
520
const unexported = [];
521
for (const name of runtimeElements) {
522
if (
523
!EXPORTED_RUNTIME_METHODS.has(name) &&
524
!EXPORTED_FUNCTIONS.has(name) &&
525
!unusedLibSymbols.has(name)
526
) {
527
unexported.push(name);
528
}
529
}
530
531
if (unexported.length || unusedLibSymbols.size) {
532
let unexportedStubs = 'var unexportedSymbols = [\n';
533
for (const sym of unexported) {
534
unexportedStubs += ` '${sym}',\n`;
535
}
536
unexportedStubs += '];\n';
537
unexportedStubs += 'unexportedSymbols.forEach(unexportedRuntimeSymbol);\n';
538
results.push(unexportedStubs);
539
}
540
}
541
542
results.unshift('// Begin runtime exports');
543
results.push('// End runtime exports');
544
return results.join('\n ') + '\n';
545
}
546
547
function exportLibrarySymbols() {
548
assert(MODULARIZE != 'instance');
549
const results = ['// Begin JS library exports'];
550
for (const ident of librarySymbols) {
551
if (EXPORT_ALL || EXPORTED_FUNCTIONS.has(ident)) {
552
results.push(exportSymbol(ident));
553
}
554
}
555
results.push('// End JS library exports');
556
return results.join('\n ') + '\n';
557
}
558
559
function exportJSSymbols() {
560
// In MODULARIZE=instance mode JS library symbols are marked with `export`
561
// at the point of declaration.
562
if (MODULARIZE == 'instance') return exportRuntimeSymbols();
563
return exportRuntimeSymbols() + ' ' + exportLibrarySymbols();
564
}
565
566
addToCompileTimeContext({
567
exportJSSymbols,
568
loadStructInfo,
569
LibraryManager,
570
librarySymbols,
571
addToLibrary,
572
cDefs,
573
cDefine,
574
C_STRUCTS,
575
C_DEFINES,
576
});
577
578