Skip to content

Commit dae65b2

Browse files
authored
Support JS symbols that are aliases of native ones (#25376)
We can then use this mechanism to replace the hacky way to we map things like `memory` -> `wasmMemory` and `__indirect_function_table` => `wasmTable`.
1 parent 5b1d579 commit dae65b2

21 files changed

+185
-80
lines changed

src/jsifier.mjs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import {
4040
warningOccured,
4141
localFile,
4242
} from './utility.mjs';
43-
import {LibraryManager, librarySymbols} from './modules.mjs';
43+
import {LibraryManager, librarySymbols, nativeAliases} from './modules.mjs';
4444

4545
const addedLibraryItems = {};
4646

@@ -128,9 +128,13 @@ function isDefined(symName) {
128128
}
129129

130130
function resolveAlias(symbol) {
131-
var value = LibraryManager.library[symbol];
132-
if (typeof value == 'string' && value[0] != '=' && LibraryManager.library.hasOwnProperty(value)) {
133-
return value;
131+
while (true) {
132+
var value = LibraryManager.library[symbol];
133+
if (typeof value == 'string' && value[0] != '=' && (LibraryManager.library.hasOwnProperty(value) || WASM_EXPORTS.has(value))) {
134+
symbol = value;
135+
} else {
136+
break;
137+
}
134138
}
135139
return symbol;
136140
}
@@ -639,6 +643,7 @@ function(${args}) {
639643
});
640644

641645
let isFunction = false;
646+
let isNativeAlias = false;
642647

643648
const postsetId = symbol + '__postset';
644649
const postset = LibraryManager.library[postsetId];
@@ -654,13 +659,22 @@ function(${args}) {
654659

655660
if (typeof snippet == 'string') {
656661
if (snippet[0] != '=') {
657-
if (LibraryManager.library[snippet]) {
662+
if (LibraryManager.library[snippet] || WASM_EXPORTS.has(snippet)) {
658663
// Redirection for aliases. We include the parent, and at runtime
659664
// make ourselves equal to it. This avoid having duplicate
660665
// functions with identical content.
661-
const aliasTarget = snippet;
662-
snippet = mangleCSymbolName(aliasTarget);
663-
deps.push(aliasTarget);
666+
const aliasTarget = resolveAlias(snippet);
667+
if (WASM_EXPORTS.has(aliasTarget)) {
668+
//printErr(`native alias: ${mangled} -> ${snippet}`);
669+
//console.error(WASM_EXPORTS);
670+
nativeAliases[mangled] = aliasTarget;
671+
snippet = undefined;
672+
isNativeAlias = true;
673+
} else {
674+
//printErr(`js alias: ${mangled} -> ${snippet}`);
675+
deps.push(aliasTarget);
676+
snippet = mangleCSymbolName(aliasTarget);
677+
}
664678
}
665679
}
666680
} else if (typeof snippet == 'object') {
@@ -728,15 +742,11 @@ function(${args}) {
728742
contentText += ';';
729743
}
730744
} else if (typeof snippet == 'undefined') {
731-
// wasmTable is kind of special. In the normal configuration we export
732-
// it from the wasm module under the name `__indirect_function_table`
733-
// but we declare it as an 'undefined' in `libcore.js`.
734-
// Since the normal export mechanism will declare this variable we don't
735-
// want the JS library version of this symbol be declared (otherwise
736-
// it would be a duplicate decl).
737-
// TODO(sbc): This is kind of hacky, we should come up with a better solution.
738-
var isDirectWasmExport = mangled == 'wasmTable';
739-
if (isDirectWasmExport) {
745+
// For JS library functions that are simply aliases of native symbols,
746+
// we don't need to generate anything here. Instead these get included
747+
// and exported alongside native symbols.
748+
// See `create_receiving` in `tools/emscripten.py`.
749+
if (isNativeAlias) {
740750
contentText = '';
741751
} else {
742752
contentText = `var ${mangled};`;
@@ -752,7 +762,7 @@ function(${args}) {
752762
contentText = `var ${mangled} = ${snippet};`;
753763
}
754764

755-
if (MODULARIZE == 'instance' && (EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled)) && !isStub) {
765+
if (contentText && MODULARIZE == 'instance' && (EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled)) && !isStub) {
756766
// In MODULARIZE=instance mode mark JS library symbols are exported at
757767
// the point of declaration.
758768
contentText = 'export ' + contentText;
@@ -775,11 +785,11 @@ function(${args}) {
775785
}
776786
}
777787

778-
let commentText = '';
779-
let docs = LibraryManager.library[symbol + '__docs'];
780788
// Add the docs if they exist and if we are actually emitting a declaration.
781789
// See the TODO about wasmTable above.
782-
if (docs && contentText != '') {
790+
let docs = LibraryManager.library[symbol + '__docs'];
791+
let commentText = '';
792+
if (contentText != '' && docs) {
783793
commentText += docs + '\n';
784794
}
785795

@@ -886,6 +896,7 @@ var proxiedFunctionTable = [
886896
'//FORWARDED_DATA:' +
887897
JSON.stringify({
888898
librarySymbols,
899+
nativeAliases,
889900
warnings: warningOccured(),
890901
asyncFuncs,
891902
libraryDefinitions: LibraryManager.libraryDefinitions,

src/lib/libbootstrap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ assert(Object.keys(LibraryManager.library).length === 0);
1515
addToLibrary({
1616
$callRuntimeCallbacks: () => {},
1717

18+
$wasmMemory: 'memory',
19+
1820
$ExitStatus: class {
1921
name = 'ExitStatus';
2022
constructor(status) {

src/lib/libcore.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,7 +1641,7 @@ addToLibrary({
16411641
#endif
16421642
}
16431643
}
1644-
1644+
exportAliases(wasmExports);
16451645
},
16461646
#endif
16471647

@@ -2150,8 +2150,6 @@ addToLibrary({
21502150
#endif // MINIMAL_RUNTIME
21512151

21522152
$asmjsMangle: (x) => {
2153-
if (x == 'memory') return 'wasmMemory';
2154-
if (x == '__indirect_function_table') return 'wasmTable';
21552153
if (x == '__main_argc_argv') {
21562154
x = 'main';
21572155
}
@@ -2287,7 +2285,14 @@ addToLibrary({
22872285
});
22882286
`,
22892287
#else
2290-
$wasmTable: undefined,
2288+
$wasmTable: '__indirect_function_table',
2289+
#endif
2290+
2291+
#if IMPORTED_MEMORY
2292+
// This gets defined in src/runtime_init_memory.js
2293+
$wasmMemory: undefined,
2294+
#else
2295+
$wasmMemory: 'memory',
22912296
#endif
22922297

22932298
$getUniqueRunDependency: (id) => {

src/lib/liblegacy.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ legacyFuncs = {
114114
},
115115

116116
// Legacy names for runtime `out`/`err` symbols.
117-
$print: 'out',
118-
$printErr: 'err',
117+
$print: '=out',
118+
$printErr: '=err',
119119

120120
// Converts a JS string to an integer base-10. Despite _s, which
121121
// suggests signaling error handling, this returns NaN on error.

src/modules.mjs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import {preprocess, processMacros} from './parseTools.mjs';
2929

3030
// List of symbols that were added from the library.
3131
export const librarySymbols = [];
32+
// Map of library symbols which are aliases for native symbols
33+
// e.g. `wasmTable` -> `__indirect_function_table`
34+
export const nativeAliases = {};
3235

3336
const srcDir = fileURLToPath(new URL('.', import.meta.url));
3437
const systemLibdir = path.join(srcDir, 'lib');
@@ -448,7 +451,6 @@ function exportRuntimeSymbols() {
448451
'err',
449452
'callMain',
450453
'abort',
451-
'wasmMemory',
452454
'wasmExports',
453455
'HEAPF32',
454456
'HEAPF64',
@@ -562,13 +564,7 @@ function exportLibrarySymbols() {
562564
assert(MODULARIZE != 'instance');
563565
const results = ['// Begin JS library exports'];
564566
for (const ident of librarySymbols) {
565-
if (EXPORT_ALL || EXPORTED_FUNCTIONS.has(ident)) {
566-
// Special case for wasmTable which can be both a JS library symbol but
567-
// also a wasm export. See isDirectWasmExport in jsifier.mjs.
568-
// FIXME: Remove this hack
569-
if (ident == 'wasmTable' && WASM_EXPORTS.has('__indirect_function_table')) {
570-
continue;
571-
}
567+
if ((EXPORT_ALL || EXPORTED_FUNCTIONS.has(ident)) && !nativeAliases[ident]) {
572568
results.push(exportSymbol(ident));
573569
}
574570
}

src/parseTools.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import {
2424
srcDir,
2525
} from './utility.mjs';
2626

27+
import { nativeAliases } from './modules.mjs';
28+
2729
const FOUR_GB = 4 * 1024 * 1024 * 1024;
2830
const WASM_PAGE_SIZE = 64 * 1024;
2931
const FLOAT_TYPES = new Set(['float', 'double']);
@@ -1155,6 +1157,17 @@ function nodeWWDetection() {
11551157
}
11561158
}
11571159

1160+
function makeExportAliases() {
1161+
var res = ''
1162+
for (var [alias, ex] of Object.entries(nativeAliases)) {
1163+
if (ASSERTIONS) {
1164+
res += ` assert(wasmExports['${ex}'], 'alias target "${ex}" not found in wasmExports');\n`;
1165+
}
1166+
res += ` globalThis['${alias}'] = wasmExports['${ex}'];\n`;
1167+
}
1168+
return res;
1169+
}
1170+
11581171
addToCompileTimeContext({
11591172
ATEXITS,
11601173
ATPRERUNS,
@@ -1204,6 +1217,7 @@ addToCompileTimeContext({
12041217
isSymbolNeeded,
12051218
makeDynCall,
12061219
makeEval,
1220+
makeExportAliases,
12071221
makeGetValue,
12081222
makeHEAPView,
12091223
makeModuleReceive,

src/runtime_common.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,9 @@ if (ENVIRONMENT_IS_NODE) {
174174
#endif // !IMPORTED_MEMORY && ASSERTIONS
175175

176176
#include "memoryprofiler.js"
177+
178+
#if !DECLARE_ASM_MODULE_EXPORTS
179+
function exportAliases(wasmExports) {
180+
{{{ makeExportAliases() }}}
181+
}
182+
#endif

src/runtime_init_memory.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
#error "this file should not be be included when IMPORTED_MEMORY is set"
1010
#endif
1111

12-
var wasmMemory;
13-
1412
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
1513

1614
function initMemory() {

src/settings_internal.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,5 @@ var OUTPUT_FORMAT = '';
275275
// Whether we should load the WASM source map at runtime.
276276
// This is enabled automatically when using -gsource-map with sanitizers.
277277
var LOAD_SOURCE_MAP = false;
278+
279+
var ALIASES = [];

test/codesize/test_codesize_file_preload.expected.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3154,12 +3154,12 @@ Module["FS_createLazyFile"] = FS_createLazyFile;
31543154
// End JS library exports
31553155
// end include: postlibrary.js
31563156
// Imports from the Wasm binary.
3157-
var _main, wasmMemory, wasmTable;
3157+
var _main, memory, __indirect_function_table, wasmMemory;
31583158

31593159
function assignWasmExports(wasmExports) {
31603160
_main = Module["_main"] = wasmExports["d"];
3161-
wasmMemory = wasmExports["b"];
3162-
wasmTable = wasmExports["__indirect_function_table"];
3161+
memory = wasmMemory = wasmExports["b"];
3162+
__indirect_function_table = wasmExports["__indirect_function_table"];
31633163
}
31643164

31653165
var wasmImports = {

0 commit comments

Comments
 (0)