Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ See docs/process.md for more on how version tagging works.
6.0.7 (in development)
----------------------

- JavaScript library symbols can now use the `__force` and `__export`
decorators to control inclusion and export visibility. (#27436)

6.0.6 - 08/05/26
----------------
- `DEFAULT_TO_CXX` is now disabled by default. This means that `em++` is now
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,9 @@ See the `library_*.js`_ files for other examples.
by ``_``. In other words ``my_func: function() {},`` becomes
``function _my_func() {}``, as all C methods in emscripten have a ``_`` prefix. Keys starting with ``$`` have the ``$``
stripped and no underscore added.
- A library symbol can use ``__force: true`` to be included even when it is
not referenced, and ``__export: true`` to be exported when included. Use
both decorators to unconditionally include and export a symbol.


.. _interacting-with-code-call-function-pointers-from-c:
Expand Down
12 changes: 8 additions & 4 deletions src/jsifier.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
debugLog,
error,
errorOccured,
extraLibraryFuncs,
isDecorator,
isJsOnlySymbol,
compileTimeContext,
Expand All @@ -41,12 +42,10 @@ import {
localFile,
timer,
} from './utility.mjs';
import {LibraryManager, librarySymbols, nativeAliases} from './modules.mjs';
import {extraExports, LibraryManager, librarySymbols, nativeAliases} from './modules.mjs';

const addedLibraryItems = {};

const extraLibraryFuncs = [];

// Experimental feature to check for invalid __deps entries.
// See `EMCC_CHECK_DEPS` in in the environment to try it out.
const CHECK_DEPS = process.env.EMCC_CHECK_DEPS;
Expand Down Expand Up @@ -700,6 +699,10 @@ function(${args}) {

librarySymbols.push(mangled);

if (!isStub && LibraryManager.library[symbol + '__export']) {
extraExports.add(mangled);
}

const original = LibraryManager.library[symbol];
let snippet = original;
const isUserSymbol = LibraryManager.library[symbol + '__user'];
Expand Down Expand Up @@ -838,7 +841,7 @@ function(${args}) {
contentText = `var ${mangled} = ${snippet};`;
}

if (contentText && MODULARIZE == 'instance' && (EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled)) && !isStub) {
if (contentText && MODULARIZE == 'instance' && (EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled) || extraExports.has(mangled)) && !isStub) {
// In MODULARIZE=instance mode mark JS library symbols are exported at
// the point of declaration.
contentText = 'export ' + contentText;
Expand Down Expand Up @@ -973,6 +976,7 @@ var proxiedFunctionTable = [
'//FORWARDED_DATA:' +
JSON.stringify({
librarySymbols,
extraExports: Array.from(extraExports),
nativeAliases,
warnings: warningOccured(),
asyncFuncs,
Expand Down
5 changes: 1 addition & 4 deletions src/lib/libasync.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ addToLibrary({
},

#if ASYNCIFY
$Asyncify__force: true,
$Asyncify__deps: ['$runAndAbortIfError', '$callUserCallback',
#if ASSERTIONS
'$createNamedFunction',
Expand Down Expand Up @@ -627,7 +628,3 @@ addToLibrary({
},
#endif // ASYNCIFY
});

if (ASYNCIFY) {
extraLibraryFuncs.push('$Asyncify');
}
42 changes: 9 additions & 33 deletions src/lib/libautodebug.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#error "Should only be included in AUTODEBUG mode"
#endif

addToLibrary({
const LibraryAutodebug = {
$log_execution: (loc) => dbg('log_execution ' + loc),
$get_i32: (loc, index, value) => {
dbg('get_i32 ' + [loc, index, value]);
Expand Down Expand Up @@ -131,36 +131,12 @@ addToLibrary({
dbg('memory_grow_post ' + [loc, result]);
return result;
},
});
};

extraLibraryFuncs.push(
'$log_execution',
'$get_i32',
'$get_i64',
'$get_f32',
'$get_f64',
'$get_funcref',
'$get_externref',
'$get_anyref',
'$get_exnref',
'$set_i32',
'$set_i64',
'$set_f32',
'$set_f64',
'$set_funcref',
'$set_externref',
'$set_anyref',
'$set_exnref',
'$load_ptr',
'$load_val_i32',
'$load_val_i64',
'$load_val_f32',
'$load_val_f64',
'$store_ptr',
'$store_val_i32',
'$store_val_i64',
'$store_val_f32',
'$store_val_f64',
'$memory_grow_pre',
'$memory_grow_post',
);
for (const symbol of Object.keys(LibraryAutodebug)) {
if (!isDecorator(symbol)) {
LibraryAutodebug[symbol + '__force'] = true;
}
}

addToLibrary(LibraryAutodebug);
6 changes: 4 additions & 2 deletions src/lib/libcore.js
Original file line number Diff line number Diff line change
Expand Up @@ -2517,7 +2517,7 @@ function autoAddDeps(lib, name) {
#if LEGACY_RUNTIME
// Library functions that were previously included as runtime functions are
// automatically included when `LEGACY_RUNTIME` is set.
extraLibraryFuncs.push(
for (const symbol of [
'$addFunction',
'$removeFunction',
'$AsciiToString',
Expand All @@ -2544,7 +2544,9 @@ extraLibraryFuncs.push(
'$stringToUTF8Array',
'$stringToUTF8',
'$lengthBytesUTF8',
);
]) {
addToLibrary({[symbol + '__force']: true}, {allowMissing: true});
}
#endif

function wrapSyscallFunction(x, library, isWasi) {
Expand Down
3 changes: 1 addition & 2 deletions src/lib/libembind_gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@ var LibraryEmbind = {
#endif
],
$emitOutput__postset: () => { addAtPostCtor('emitOutput()'); },
$emitOutput__force: true,
$emitOutput: () => {
for (const typeId in awaitingDependencies) {
throwBindingError(`Missing binding for type: '${getTypeName(typeId)}' typeId: ${typeId}`);
Expand All @@ -936,6 +937,4 @@ var LibraryEmbind = {
$PureVirtualError: () => { throw new Error('stub function should not be called'); },
};

extraLibraryFuncs.push('$emitOutput');

addToLibrary(LibraryEmbind);
4 changes: 4 additions & 0 deletions src/lib/libfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
*/

var LibraryFS = {
#if FORCE_FILESYSTEM
// Include FS even when it is not referenced by compiled code.
$FS__force: true,
#endif
$FS__deps: ['$randomFill', '$PATH', '$PATH_FS', '$TTY', '$MEMFS',
'$FS_modeStringToFlags',
'$FS_fileDataToTypedArray',
Expand Down
7 changes: 0 additions & 7 deletions src/lib/libfs_shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,3 @@ addToLibrary({
$FS_readFile__deps: ['$FS'],
$FS_readFile: 'FS.readFile',
});

// Normally only the FS things that the compiler sees are needed are included.
// FORCE_FILESYSTEM makes us always include the FS object, which lets the user
// call APIs on it from JS freely.
if (FORCE_FILESYSTEM) {
extraLibraryFuncs.push('$FS');
}
3 changes: 1 addition & 2 deletions src/lib/libglemu.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var LibraryGLEmulation = {
'glVertexAttribPointer', 'glActiveTexture', '$stringToNewUTF8',
'$ptrToString', '$getEmscriptenSupportedExtensions',
],
$GLEmulation__force: true,
$GLEmulation__postset: `
// Forward declare GL functions that are overridden by GLEmulation.
/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDrawArrays;
Expand Down Expand Up @@ -3942,8 +3943,6 @@ var LibraryGLEmulation = {
gluOrtho2D: (left, right, bottom, top) => _glOrtho(left, right, bottom, top, -1, 1),
};

extraLibraryFuncs.push('$GLEmulation');

recordGLProcAddressGet(LibraryGLEmulation);

addToLibrary(LibraryGLEmulation);
4 changes: 4 additions & 0 deletions src/lib/libwasmfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
*/

addToLibrary({
#if FORCE_FILESYSTEM
// Include FS even when it is not referenced by compiled code.
$FS__force: true,
#endif
$MEMFS__deps: ['wasmfs_create_memory_backend'],
$MEMFS: {
createBackend(opts) {
Expand Down
6 changes: 5 additions & 1 deletion src/modules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {preprocess, processMacros} from './parseTools.mjs';

// List of symbols that were added from the library.
export const librarySymbols = [];
// Library symbols exported via the `__export` decorator.
export const extraExports = new Set();
// Map of library symbols which are aliases for native symbols
// e.g. `wasmTable` -> `__indirect_function_table`
export const nativeAliases = {};
Expand Down Expand Up @@ -575,6 +577,7 @@ function exportRuntimeSymbols() {
if (
!EXPORTED_RUNTIME_METHODS.has(name) &&
!EXPORTED_FUNCTIONS.has(name) &&
!extraExports.has(name) &&
!unusedLibSymbols.has(name)
) {
unexported.push(name);
Expand All @@ -601,7 +604,7 @@ 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]) {
if ((EXPORT_ALL || EXPORTED_FUNCTIONS.has(ident) || extraExports.has(ident)) && !nativeAliases[ident]) {
results.push(exportSymbol(ident));
}
}
Expand All @@ -621,6 +624,7 @@ addToCompileTimeContext({
loadStructInfo,
LibraryManager,
librarySymbols,
extraExports,
addToLibrary,
cDefs,
cDefine,
Expand Down
12 changes: 11 additions & 1 deletion src/utility.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ function range(size) {
return Array.from(Array(size).keys());
}

export const extraLibraryFuncs = [];

export function mergeInto(obj, other, options = null) {
if (options) {
// check for unintended symbol redefinition
Expand Down Expand Up @@ -148,11 +150,11 @@ export function mergeInto(obj, other, options = null) {
}

const index = key.lastIndexOf('__');
const decorated = key.slice(0, index);
const decoratorName = key.slice(index);
const type = typeof other[key];

if (decoratorName == '__async') {
const decorated = key.slice(0, index);
if (isJsOnlySymbol(decorated)) {
error(`__async decorator applied to JS symbol: ${decorated}`);
}
Expand Down Expand Up @@ -188,12 +190,18 @@ export function mergeInto(obj, other, options = null) {
__user: 'boolean',
__async: ['string', 'boolean'],
__i53abi: 'boolean',
__export: 'boolean',
__force: 'boolean',
};
const expected = decoratorTypes[decoratorName];
if (type !== expected && !expected.includes(type)) {
error(`Decorator (${key}) has wrong type. Expected '${expected}' not '${type}'`);
}
}

if (decoratorName === '__force' && other[key]) {
extraLibraryFuncs.push(decorated);
}
}
}

Expand All @@ -219,6 +227,8 @@ export const decoratorSuffixes = [
'__user',
'__async',
'__i53abi',
'__export',
'__force',
];

export function isDecorator(ident) {
Expand Down
Loading
Loading