-
Notifications
You must be signed in to change notification settings - Fork 3.4k
JS optimizer: Fix scoping issues with non-defined functions #23159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
2c778e5
aa330fe
6ece43b
f2bcfe4
0f22581
e8da68e
af5ab83
33447f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| [ | ||
| { | ||
| "name": "emcc$defun$arrowed", | ||
| "reaches": [], | ||
| "root": true | ||
| }, | ||
| { | ||
| "name": "emcc$defun$bar", | ||
| "reaches": [], | ||
| "root": true | ||
| }, | ||
| { | ||
| "name": "emcc$defun$caller", | ||
| "reaches": [ | ||
| "emcc$defun$foo" | ||
| ], | ||
| "root": true | ||
| }, | ||
| { | ||
| "name": "emcc$defun$foo", | ||
| "reaches": [] | ||
| } | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
|
|
||
| function foo() {} | ||
|
|
||
| function bar() {} | ||
|
|
||
| // caller() calls foo(). There is also another function called "caller", down | ||
| // below, which should not confuse us (if it does, nothing would refer to foo, | ||
| // and instead we'd think the toplevel caller calls bar). | ||
| function caller() { | ||
| foo(); | ||
| } | ||
|
|
||
| caller(); | ||
|
|
||
| var object = { | ||
| method() { | ||
| function caller(data) { | ||
| bar(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| // Similar, with an arrow function. This should also not confuse us (it would | ||
| // make "caller" refer to "arrowed". | ||
|
|
||
| function arrowed() {} | ||
|
|
||
| var arrow = () => { | ||
| function caller(data) { | ||
| arrowed(); | ||
| } | ||
| } | ||
|
|
||
| wasmImports = {}; | ||
|
|
||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,9 +65,13 @@ function simpleWalk(node, cs) { | |
| } | ||
| } | ||
|
|
||
| // Full post-order walk, calling a single function for all types. | ||
| function fullWalk(node, c) { | ||
| visitChildren(node, (child) => fullWalk(child, c)); | ||
| // Full post-order walk, calling a single function for all types. If |pre| is | ||
| // provided, it is called in pre-order (before children). | ||
| function fullWalk(node, c, pre) { | ||
| if (pre) { | ||
| pre(node); | ||
| } | ||
| visitChildren(node, (child) => fullWalk(child, c, pre)); | ||
| c(node); | ||
| } | ||
|
|
||
|
|
@@ -699,6 +703,13 @@ function emitDCEGraph(ast) { | |
| } | ||
| } | ||
|
|
||
| // We track defined functions very carefully, so that we can remove them and | ||
| // the things they call, but other function scopes (like arrow functions and | ||
| // object methods) are trickier to track (object methods require knowing what | ||
| // object a function name is called on), so we do not track those. We consider | ||
| // all content inside them as top-level, which means it is used. | ||
| var specialScopes = 0; | ||
|
|
||
| fullWalk(ast, (node) => { | ||
| if (isWasmImportsAssign(node)) { | ||
| const assignedObject = getWasmImportsValue(node); | ||
|
|
@@ -788,11 +799,14 @@ function emitDCEGraph(ast) { | |
| emptyOut(node); | ||
| } | ||
| } else if (node.type === 'FunctionDeclaration') { | ||
| defuns.push(node); | ||
| const name = node.id.name; | ||
| nameToGraphName[name] = getGraphName(name, 'defun'); | ||
| emptyOut(node); // ignore this in the second pass; we scan defuns separately | ||
| if (!specialScopes) { | ||
| defuns.push(node); | ||
| const name = node.id.name; | ||
| nameToGraphName[name] = getGraphName(name, 'defun'); | ||
| emptyOut(node); // ignore this in the second pass; we scan defuns separately | ||
| } | ||
| } else if (node.type === 'ArrowFunctionExpression') { | ||
| specialScopes--; | ||
| // Check if this is the minimal runtime exports function, which looks like | ||
| // (output) => { var wasmExports = output.instance.exports; | ||
| if ( | ||
|
|
@@ -857,9 +871,19 @@ function emitDCEGraph(ast) { | |
| } | ||
| } | ||
| } | ||
| } else if (node.type === 'Property' && node.method) { | ||
|
||
| specialScopes--; | ||
| } | ||
| }, (node) => { | ||
| // Pre-walking logic. We note special scopes (see above). | ||
| if (node.type === 'ArrowFunctionExpression' || | ||
| (node.type === 'Property' && node.method)) { | ||
| specialScopes++; | ||
| } | ||
| }); | ||
| // must find the info we need | ||
| // Scoping must balance out. | ||
| assert(specialScopes === 0); | ||
| // We must have found the info we need. | ||
| assert( | ||
| foundWasmImportsAssign, | ||
| 'could not find the assignment to "wasmImports". 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)', | ||
|
|
@@ -870,6 +894,7 @@ function emitDCEGraph(ast) { | |
| saveAsmExport(exp[0], exp[1]); | ||
| } | ||
| } | ||
|
|
||
| // Second pass: everything used in the toplevel scope is rooted; | ||
| // things used in defun scopes create links | ||
| function getGraphName(name, what) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can give this (and the other tests 1-5) better names? Can be followup maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll rename the new one in this PR. I can look at the others later.