Only export user-requests symbols on the Module object. NFC
Some exports are only needed internally and those don't need to be
exported on the Module object. This is save some space and avoids
unexpected exports on the public-facing Module.
diff --git a/emscripten.py b/emscripten.py
index 01b92ad..8c3d56a 100644
--- a/emscripten.py
+++ b/emscripten.py
@@ -799,6 +799,15 @@
if name == '__cpp_exception':
continue
mangled = asmjs_mangle(name)
+ # We only export onto the Module object of the user requested the export.
+ # Some exports are used internally by library functions and don't need to be
+ # publically exported.
+ # The exception is `dynCall_` functions, which are dynamically used via the
+ # Module object (see dynCallLegacy in library.js).
+ if mangled in building.user_requested_exports or name.startswith('dynCall_'):
+ lhs = f'{mangled} = Module["{mangled}"]'
+ else:
+ lhs = f'{mangled}'
# The emscripten stack functions are called very early (by writeStackCookie) before
# the runtime is initialized so we can't create these wrappers that check for
# runtimeInitialized.
@@ -808,27 +817,27 @@
if delay_assignment:
wrappers.append('''\
/** @type {function(...*):?} */
-var %(mangled)s = Module["%(mangled)s"] = createExportWrapper("%(name)s");
-''' % {'mangled': mangled, 'name': name})
+var %(lhs)s = createExportWrapper("%(name)s");
+''' % {'lhs': lhs, 'name': name})
else:
wrappers.append('''\
/** @type {function(...*):?} */
-var %(mangled)s = Module["%(mangled)s"] = createExportWrapper("%(name)s", asm);
-''' % {'mangled': mangled, 'name': name})
+var %(lhs)s = createExportWrapper("%(name)s", asm);
+''' % {'lhs': lhs, 'name': name})
elif delay_assignment:
# With assertions disabled the wrapper will replace the global var and Module var on
# first use.
wrappers.append('''\
/** @type {function(...*):?} */
-var %(mangled)s = Module["%(mangled)s"] = function() {
- return (%(mangled)s = Module["%(mangled)s"] = Module["asm"]["%(name)s"]).apply(null, arguments);
+var %(lhs)s = function() {
+ return (%(lhs)s = Module["asm"]["%(name)s"]).apply(null, arguments);
};
-''' % {'mangled': mangled, 'name': name})
+''' % {'lhs': lhs, 'name': name})
else:
wrappers.append('''\
/** @type {function(...*):?} */
-var %(mangled)s = Module["%(mangled)s"] = asm["%(name)s"]
-''' % {'mangled': mangled, 'name': name})
+var %(lhs)s = asm["%(name)s"]
+''' % {'lhs': lhs, 'name': name})
return wrappers
diff --git a/tests/code_size/random_printf_wasm.json b/tests/code_size/random_printf_wasm.json
index e336a25..2cf0e32 100644
--- a/tests/code_size/random_printf_wasm.json
+++ b/tests/code_size/random_printf_wasm.json
@@ -1,6 +1,6 @@
{
- "a.html": 12854,
- "a.html.gz": 6948,
- "total": 12854,
- "total_gz": 6948
+ "a.html": 12862,
+ "a.html.gz": 6952,
+ "total": 12862,
+ "total_gz": 6952
}
diff --git a/tests/code_size/random_printf_wasm2js.json b/tests/code_size/random_printf_wasm2js.json
index ab1bf8f..cd17081 100644
--- a/tests/code_size/random_printf_wasm2js.json
+++ b/tests/code_size/random_printf_wasm2js.json
@@ -1,6 +1,6 @@
{
- "a.html": 17360,
- "a.html.gz": 7495,
- "total": 17360,
- "total_gz": 7495
+ "a.html": 17362,
+ "a.html.gz": 7499,
+ "total": 17362,
+ "total_gz": 7499
}
diff --git a/tests/other/metadce/test_metadce_cxx_ctors1.jssize b/tests/other/metadce/test_metadce_cxx_ctors1.jssize
index 886b510..c50698c 100644
--- a/tests/other/metadce/test_metadce_cxx_ctors1.jssize
+++ b/tests/other/metadce/test_metadce_cxx_ctors1.jssize
@@ -1 +1 @@
-26202
+25730
diff --git a/tests/other/metadce/test_metadce_cxx_ctors2.jssize b/tests/other/metadce/test_metadce_cxx_ctors2.jssize
index 51d0989..a28728c 100644
--- a/tests/other/metadce/test_metadce_cxx_ctors2.jssize
+++ b/tests/other/metadce/test_metadce_cxx_ctors2.jssize
@@ -1 +1 @@
-26166
+25694
diff --git a/tests/other/metadce/test_metadce_cxx_except.jssize b/tests/other/metadce/test_metadce_cxx_except.jssize
index a63ced0..983149f 100644
--- a/tests/other/metadce/test_metadce_cxx_except.jssize
+++ b/tests/other/metadce/test_metadce_cxx_except.jssize
@@ -1 +1 @@
-31595
+31079
diff --git a/tests/other/metadce/test_metadce_cxx_except_wasm.jssize b/tests/other/metadce/test_metadce_cxx_except_wasm.jssize
index 6f093bd..20651b1 100644
--- a/tests/other/metadce/test_metadce_cxx_except_wasm.jssize
+++ b/tests/other/metadce/test_metadce_cxx_except_wasm.jssize
@@ -1 +1 @@
-26356
+25708
diff --git a/tests/other/metadce/test_metadce_cxx_mangle.jssize b/tests/other/metadce/test_metadce_cxx_mangle.jssize
index b3c1dad..983149f 100644
--- a/tests/other/metadce/test_metadce_cxx_mangle.jssize
+++ b/tests/other/metadce/test_metadce_cxx_mangle.jssize
@@ -1 +1 @@
-31695
+31079
diff --git a/tests/other/metadce/test_metadce_cxx_noexcept.jssize b/tests/other/metadce/test_metadce_cxx_noexcept.jssize
index 886b510..c50698c 100644
--- a/tests/other/metadce/test_metadce_cxx_noexcept.jssize
+++ b/tests/other/metadce/test_metadce_cxx_noexcept.jssize
@@ -1 +1 @@
-26202
+25730
diff --git a/tests/other/metadce/test_metadce_hello_O0.jssize b/tests/other/metadce/test_metadce_hello_O0.jssize
index 7d930f2..da9b57e 100644
--- a/tests/other/metadce/test_metadce_hello_O0.jssize
+++ b/tests/other/metadce/test_metadce_hello_O0.jssize
@@ -1 +1 @@
-26698
+26111
diff --git a/tests/other/metadce/test_metadce_hello_O1.jssize b/tests/other/metadce/test_metadce_hello_O1.jssize
index 55c92ab..8f067af 100644
--- a/tests/other/metadce/test_metadce_hello_O1.jssize
+++ b/tests/other/metadce/test_metadce_hello_O1.jssize
@@ -1 +1 @@
-9245
+8709
diff --git a/tests/other/metadce/test_metadce_hello_O2.jssize b/tests/other/metadce/test_metadce_hello_O2.jssize
index 28d2ab2..9c64c70 100644
--- a/tests/other/metadce/test_metadce_hello_O2.jssize
+++ b/tests/other/metadce/test_metadce_hello_O2.jssize
@@ -1 +1 @@
-6640
+6168
diff --git a/tests/other/metadce/test_metadce_hello_O3.jssize b/tests/other/metadce/test_metadce_hello_O3.jssize
index a77fd92..c4990d1 100644
--- a/tests/other/metadce/test_metadce_hello_O3.jssize
+++ b/tests/other/metadce/test_metadce_hello_O3.jssize
@@ -1 +1 @@
-6000
+5908
diff --git a/tests/other/metadce/test_metadce_hello_Os.jssize b/tests/other/metadce/test_metadce_hello_Os.jssize
index a77fd92..c4990d1 100644
--- a/tests/other/metadce/test_metadce_hello_Os.jssize
+++ b/tests/other/metadce/test_metadce_hello_Os.jssize
@@ -1 +1 @@
-6000
+5908
diff --git a/tests/other/metadce/test_metadce_hello_Oz.jssize b/tests/other/metadce/test_metadce_hello_Oz.jssize
index d3d9753..a328b99 100644
--- a/tests/other/metadce/test_metadce_hello_Oz.jssize
+++ b/tests/other/metadce/test_metadce_hello_Oz.jssize
@@ -1 +1 @@
-5959
+5867
diff --git a/tests/other/metadce/test_metadce_hello_export_nothing.jssize b/tests/other/metadce/test_metadce_hello_export_nothing.jssize
index ae6d064..8831f9f 100644
--- a/tests/other/metadce/test_metadce_hello_export_nothing.jssize
+++ b/tests/other/metadce/test_metadce_hello_export_nothing.jssize
@@ -1 +1 @@
-4670
+4578
diff --git a/tests/other/metadce/test_metadce_libcxxabi_message_O3.jssize b/tests/other/metadce/test_metadce_libcxxabi_message_O3.jssize
index 7663e73..1492556 100644
--- a/tests/other/metadce/test_metadce_libcxxabi_message_O3.jssize
+++ b/tests/other/metadce/test_metadce_libcxxabi_message_O3.jssize
@@ -1 +1 @@
-5209
+5117
diff --git a/tests/other/metadce/test_metadce_mem_O3.jssize b/tests/other/metadce/test_metadce_mem_O3.jssize
index 6728503..3de49f2 100644
--- a/tests/other/metadce/test_metadce_mem_O3.jssize
+++ b/tests/other/metadce/test_metadce_mem_O3.jssize
@@ -1 +1 @@
-6152
+6032
diff --git a/tests/other/metadce/test_metadce_mem_O3_grow.jssize b/tests/other/metadce/test_metadce_mem_O3_grow.jssize
index af08b90..a153ee2 100644
--- a/tests/other/metadce/test_metadce_mem_O3_grow.jssize
+++ b/tests/other/metadce/test_metadce_mem_O3_grow.jssize
@@ -1 +1 @@
-6483
+6364
diff --git a/tests/other/metadce/test_metadce_minimal_O0.jssize b/tests/other/metadce/test_metadce_minimal_O0.jssize
index 09b20f4..bb4ad0b 100644
--- a/tests/other/metadce/test_metadce_minimal_O0.jssize
+++ b/tests/other/metadce/test_metadce_minimal_O0.jssize
@@ -1 +1 @@
-21387
+20779
diff --git a/tests/other/metadce/test_metadce_minimal_O1.jssize b/tests/other/metadce/test_metadce_minimal_O1.jssize
index a3d5648..6902890 100644
--- a/tests/other/metadce/test_metadce_minimal_O1.jssize
+++ b/tests/other/metadce/test_metadce_minimal_O1.jssize
@@ -1 +1 @@
-5710
+5174
diff --git a/tests/other/metadce/test_metadce_minimal_O2.jssize b/tests/other/metadce/test_metadce_minimal_O2.jssize
index 2996989..54bbfbf 100644
--- a/tests/other/metadce/test_metadce_minimal_O2.jssize
+++ b/tests/other/metadce/test_metadce_minimal_O2.jssize
@@ -1 +1 @@
-4438
+3966
diff --git a/tests/other/metadce/test_metadce_minimal_O3.jssize b/tests/other/metadce/test_metadce_minimal_O3.jssize
index dceb0b3..7593b89 100644
--- a/tests/other/metadce/test_metadce_minimal_O3.jssize
+++ b/tests/other/metadce/test_metadce_minimal_O3.jssize
@@ -1 +1 @@
-3886
+3794
diff --git a/tests/other/metadce/test_metadce_minimal_Os.jssize b/tests/other/metadce/test_metadce_minimal_Os.jssize
index dceb0b3..7593b89 100644
--- a/tests/other/metadce/test_metadce_minimal_Os.jssize
+++ b/tests/other/metadce/test_metadce_minimal_Os.jssize
@@ -1 +1 @@
-3886
+3794
diff --git a/tests/other/metadce/test_metadce_minimal_Oz.jssize b/tests/other/metadce/test_metadce_minimal_Oz.jssize
index dceb0b3..7593b89 100644
--- a/tests/other/metadce/test_metadce_minimal_Oz.jssize
+++ b/tests/other/metadce/test_metadce_minimal_Oz.jssize
@@ -1 +1 @@
-3886
+3794
diff --git a/tests/other/metadce/test_metadce_minimal_pthreads.jssize b/tests/other/metadce/test_metadce_minimal_pthreads.jssize
index dc3f830..2ba5aa4 100644
--- a/tests/other/metadce/test_metadce_minimal_pthreads.jssize
+++ b/tests/other/metadce/test_metadce_minimal_pthreads.jssize
@@ -1 +1 @@
-15974
+15585
diff --git a/tests/other/test_unoptimized_code_size.js.size b/tests/other/test_unoptimized_code_size.js.size
index 1d99a9d..ff8c7e4 100644
--- a/tests/other/test_unoptimized_code_size.js.size
+++ b/tests/other/test_unoptimized_code_size.js.size
@@ -1 +1 @@
-91192
+90759
diff --git a/tests/other/test_unoptimized_code_size_no_asserts.js.size b/tests/other/test_unoptimized_code_size_no_asserts.js.size
index 74ed601..f9a4636 100644
--- a/tests/other/test_unoptimized_code_size_no_asserts.js.size
+++ b/tests/other/test_unoptimized_code_size_no_asserts.js.size
@@ -1 +1 @@
-53126
+52864
diff --git a/tools/acorn-optimizer.js b/tools/acorn-optimizer.js
index 5c1c10d..25dac7e 100755
--- a/tools/acorn-optimizer.js
+++ b/tools/acorn-optimizer.js
@@ -611,14 +611,25 @@
// var asmLibraryArg = { "abort": abort, "assert": assert, [..] };
//
// The exports are trickier, as they have a different form whether or not
- // async compilation is enabled. It can be either:
+ // async compilation is enabled, and whether they are exported to the outside
+ // module or not. They can take any of the following forms:
//
// var _malloc = Module["_malloc"] = asm["_malloc"];
//
// or
//
+ // var _malloc = asm["_malloc"];
+ //
+ // or
+ //
// var _malloc = Module["_malloc"] = (function() {
- // return Module["asm"]["_malloc"].apply(null, arguments);
+ // return (_malloc = Module["_malloc"] = Module["asm"]["_malloc"]).apply(null, arguments);
+ // });
+ //
+ // or
+ //
+ // var _malloc = Module["_malloc"] = (function() {
+ // return (_malloc = Module["asm"]["_malloc"]).apply(null, arguments);
// });
//
// or, in the minimal runtime, it looks like
@@ -675,38 +686,44 @@
const item = node.declarations[0];
const name = item.id.name;
const value = item.init;
+ if (!value) {
+ return;
+ }
+ let payload = value;
if (value && value.type === 'AssignmentExpression') {
const assigned = value.left;
- if (isModuleUse(assigned) && getAsmOrModuleUseName(assigned) === name) {
- // this is
- // var x = Module['x'] = ?
- // which looks like a wasm export being received. confirm with the asm use
- let found = 0;
- let asmName;
- fullWalk(value.right, function(node) {
- if (isAsmUse(node)) {
- found++;
- asmName = getAsmOrModuleUseName(node);
- }
- });
- // in the wasm backend, the asm name may have one fewer "_" prefixed
- if (found === 1) {
- // this is indeed an export
- // the asmName is what the wasm provides directly; the outside JS
- // name may be slightly different (extra "_" in wasm backend)
- saveAsmExport(name, asmName);
- emptyOut(node); // ignore this in the second pass; this does not root
- return;
- }
- if (value.right.type === 'Literal') {
- // this is
- // var x = Module['x'] = 1234;
- // this form occurs when global addresses are exported from the
- // module. It doesn't constitute a usage.
- assert(typeof value.right.value === 'number');
- emptyOut(node);
- }
+ if (!isModuleUse(assigned) || getAsmOrModuleUseName(assigned) !== name) {
+ return;
}
+ // this is
+ // var x = Module['x'] = ?
+ payload = value.right;
+ if (payload.type === 'Literal') {
+ // this is
+ // var x = Module['x'] = 1234;
+ // this form occurs when global addresses are exported from the
+ // module. It doesn't constitute a usage.
+ assert(typeof payload.value === 'number', payload);
+ emptyOut(node);
+ return;
+ }
+ }
+ // which looks like a wasm export being received. confirm with the asm use
+ let asmName;
+ if (isAsmUse(payload)) {
+ asmName = getAsmOrModuleUseName(payload);
+ } else {
+ // console.error("looking for: " + name);
+ asmName = isExportWrapperFunction(payload);
+ }
+ if (asmName) {
+ // console.error("found export: " + name);
+ // this is indeed an export
+ // the asmName is what the wasm provides directly; the outside JS
+ // name may be slightly different (extra "_" in wasm backend)
+ saveAsmExport(name, asmName);
+ emptyOut(node); // ignore this in the second pass; this does not root
+ return;
}
}
// A variable declaration that has no initial values can be ignored in
@@ -781,6 +798,8 @@
assert(foundAsmLibraryArgAssign, 'could not find the assigment to "asmLibraryArg". perhaps --pre-js or --post-js code moved it out of the global scope? (things like that should be done after emcc runs, as they do not need to be run through the optimizer which is the special thing about --pre-js/--post-js code)');
// Read exports that were declared in extraInfo
if (extraInfo) {
+ // console.error('extraInfo: ');
+ // console.error(extraInfo);
for (const exp of extraInfo.exports) {
saveAsmExport(exp[0], exp[1]);
}
@@ -835,6 +854,7 @@
// any remaining asm uses are always rooted in any case
const name = getAsmOrModuleUseName(node);
if (exportNameToGraphName.hasOwnProperty(name)) {
+ // console.error("rooting: " + name);
infos[exportNameToGraphName[name]].root = true;
}
return;
@@ -891,9 +911,47 @@
print(JSON.stringify(graph, null, ' '));
}
+// Matches functions of the form:
+// var stackSave = function() {
+// return (stackSave = Module["asm"]["stackSave"]).apply(null, arguments);
+// };
+function isExportWrapperFunction(node) {
+ if (node.type !== 'FunctionExpression') {
+ return false;
+ }
+ const body = node.body.body;
+ if (body.length !== 1 || body[0].type !== 'ReturnStatement') {
+ return false;
+ }
+ const returnVal = body[0];
+ if (returnVal.argument.type !== 'CallExpression') {
+ return false;
+ }
+ const callee = returnVal.argument.callee;
+ if (callee.type !== 'MemberExpression') {
+ return false;
+ }
+ let value = callee.object;
+ if (value.type === 'ParenthesizedExpression') {
+ value = value.expression;
+ }
+ if (value.type !== 'AssignmentExpression') {
+ return false;
+ }
+ while (value.type === 'AssignmentExpression') {
+ value = value.right;
+ }
+ if (!isAsmUse(value)) {
+ return false;
+ }
+ return getAsmOrModuleUseName(value);
+}
+
// Apply graph removals from running wasm-metadce
function applyDCEGraphRemovals(ast) {
const unused = new Set(extraInfo.unused);
+ // console.error("unused:");
+ // console.error(unused);
fullWalk(ast, (node) => {
if (isAsmLibraryArgAssign(node)) {
@@ -904,7 +962,7 @@
const full = 'emcc$import$' + name;
return !(unused.has(full) && !hasSideEffects(value));
});
- } else if (node.type === 'AssignmentExpression') {
+ } else if (false && node.type === 'AssignmentExpression') {
// when we assign to a thing we don't need, we can just remove the assign
const target = node.left;
if (isAsmUse(target) || isModuleUse(target)) {
@@ -917,6 +975,31 @@
convertToNothingInVarInit(node);
}
}
+ } else if (node.type === 'VariableDeclaration') {
+ // In the regular runtime we several different shapes for the export
+ // declarations:
+ // var _x = Module["_x"] = function() {
+ // return (_x = Module["_x"] = Module["asm"]["x"]).apply(null, arguments);
+ // };
+ // var _x = function() {
+ // return (_x = Module["asm"]["x"]).apply(null, arguments);
+ // };
+ // var _x = Module["_x"] = asm["x"];
+ // var _x = asm["x"];
+ if (node.declarations.length === 1) {
+ const decl = node.declarations[0];
+ const name = decl.id.name;
+ const full = 'emcc$export$' + name;
+ if (unused.has(full)) {
+ let init = decl.init;
+ if (init.type == 'AssignmentExpression') {
+ init = init.right;
+ }
+ if (isExportWrapperFunction(init) || isAsmUse(init)) {
+ emptyOut(node);
+ }
+ }
+ }
} else if (node.type === 'ExpressionStatement') {
const expr = node.expression;
// In the minimal runtime code pattern we have just