From edae1ba2ea17db86de3a9aeb1ef816cd8e49baef Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Sat, 29 May 2021 06:19:47 +0200 Subject: [PATCH] Always request a new tree-shaking pass when deoptimizations of a node are first included (#4111) --- src/Module.ts | 2 ++ src/ast/nodes/AssignmentExpression.ts | 1 + src/ast/nodes/AssignmentPattern.ts | 1 + src/ast/nodes/CallExpression.ts | 1 + src/ast/nodes/ForInStatement.ts | 1 + src/ast/nodes/ForOfStatement.ts | 1 + src/ast/nodes/Identifier.ts | 1 + src/ast/nodes/MemberExpression.ts | 1 + src/ast/nodes/NewExpression.ts | 1 + src/ast/nodes/Property.ts | 1 + src/ast/nodes/RestElement.ts | 1 + src/ast/nodes/SpreadElement.ts | 1 + src/ast/nodes/UnaryExpression.ts | 1 + src/ast/nodes/UpdateExpression.ts | 1 + src/ast/nodes/YieldExpression.ts | 6 +++++- .../_config.js | 10 ++++++++++ .../deoptimize-request-treeshaking-pass/main.js | 17 +++++++++++++++++ 17 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/function/samples/deoptimize-request-treeshaking-pass/_config.js create mode 100644 test/function/samples/deoptimize-request-treeshaking-pass/main.js diff --git a/src/Module.ts b/src/Module.ts index 0755ba90e23..a6071973b92 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -109,6 +109,7 @@ export interface AstContext { moduleContext: string; nodeConstructors: { [name: string]: typeof NodeBase }; options: NormalizedInputOptions; + requestTreeshakingPass: () => void; traceExport: (name: string) => Variable | null; traceVariable: (name: string) => Variable | null; usesTopLevelAwait: boolean; @@ -726,6 +727,7 @@ export default class Module { moduleContext: this.context, nodeConstructors, options: this.options, + requestTreeshakingPass: () => (this.graph.needsTreeshakingPass = true), traceExport: this.getVariableForExportName.bind(this), traceVariable: this.traceVariable.bind(this), usesTopLevelAwait: false, diff --git a/src/ast/nodes/AssignmentExpression.ts b/src/ast/nodes/AssignmentExpression.ts index ec9501ca078..28111010856 100644 --- a/src/ast/nodes/AssignmentExpression.ts +++ b/src/ast/nodes/AssignmentExpression.ts @@ -126,5 +126,6 @@ export default class AssignmentExpression extends NodeBase { this.deoptimized = true; this.left.deoptimizePath(EMPTY_PATH); this.right.deoptimizePath(UNKNOWN_PATH); + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/AssignmentPattern.ts b/src/ast/nodes/AssignmentPattern.ts index 9ee9dd018c2..9386e07f325 100644 --- a/src/ast/nodes/AssignmentPattern.ts +++ b/src/ast/nodes/AssignmentPattern.ts @@ -48,5 +48,6 @@ export default class AssignmentPattern extends NodeBase implements PatternNode { this.deoptimized = true; this.left.deoptimizePath(EMPTY_PATH); this.right.deoptimizePath(UNKNOWN_PATH); + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/CallExpression.ts b/src/ast/nodes/CallExpression.ts index 14fdb7c6002..4beabc61ad0 100644 --- a/src/ast/nodes/CallExpression.ts +++ b/src/ast/nodes/CallExpression.ts @@ -298,6 +298,7 @@ export default class CallExpression extends NodeBase implements DeoptimizableEnt // This will make sure all properties of parameters behave as "unknown" argument.deoptimizePath(UNKNOWN_PATH); } + this.context.requestTreeshakingPass(); } private getReturnExpression( diff --git a/src/ast/nodes/ForInStatement.ts b/src/ast/nodes/ForInStatement.ts index a8e8cc9958d..ac375c0c3ec 100644 --- a/src/ast/nodes/ForInStatement.ts +++ b/src/ast/nodes/ForInStatement.ts @@ -65,5 +65,6 @@ export default class ForInStatement extends StatementBase { protected applyDeoptimizations(): void { this.deoptimized = true; this.left.deoptimizePath(EMPTY_PATH); + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/ForOfStatement.ts b/src/ast/nodes/ForOfStatement.ts index fc7c5b02a7d..fc7a54a46b7 100644 --- a/src/ast/nodes/ForOfStatement.ts +++ b/src/ast/nodes/ForOfStatement.ts @@ -50,5 +50,6 @@ export default class ForOfStatement extends StatementBase { protected applyDeoptimizations(): void { this.deoptimized = true; this.left.deoptimizePath(EMPTY_PATH); + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/Identifier.ts b/src/ast/nodes/Identifier.ts index 27078d6ce28..97a4f560092 100644 --- a/src/ast/nodes/Identifier.ts +++ b/src/ast/nodes/Identifier.ts @@ -178,6 +178,7 @@ export default class Identifier extends NodeBase implements PatternNode { this.deoptimized = true; if (this.variable !== null && this.variable instanceof LocalVariable) { this.variable.consolidateInitializers(); + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/MemberExpression.ts b/src/ast/nodes/MemberExpression.ts index 88613f0896a..c585d5c9aa0 100644 --- a/src/ast/nodes/MemberExpression.ts +++ b/src/ast/nodes/MemberExpression.ts @@ -329,6 +329,7 @@ export default class MemberExpression extends NodeBase implements DeoptimizableE SHARED_RECURSION_TRACKER ); } + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/NewExpression.ts b/src/ast/nodes/NewExpression.ts index 8840b65d405..633ad8a124a 100644 --- a/src/ast/nodes/NewExpression.ts +++ b/src/ast/nodes/NewExpression.ts @@ -46,5 +46,6 @@ export default class NewExpression extends NodeBase { // This will make sure all properties of parameters behave as "unknown" argument.deoptimizePath(UNKNOWN_PATH); } + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/Property.ts b/src/ast/nodes/Property.ts index a9877fe403f..3f5649669d1 100644 --- a/src/ast/nodes/Property.ts +++ b/src/ast/nodes/Property.ts @@ -46,6 +46,7 @@ export default class Property extends MethodBase implements PatternNode { this.deoptimized = true; if (this.declarationInit !== null) { this.declarationInit.deoptimizePath([UnknownKey, UnknownKey]); + this.context.requestTreeshakingPass(); } } } diff --git a/src/ast/nodes/RestElement.ts b/src/ast/nodes/RestElement.ts index 6ce201cdfa2..de3fb0dc7b0 100644 --- a/src/ast/nodes/RestElement.ts +++ b/src/ast/nodes/RestElement.ts @@ -37,6 +37,7 @@ export default class RestElement extends NodeBase implements PatternNode { this.deoptimized = true; if (this.declarationInit !== null) { this.declarationInit.deoptimizePath([UnknownKey, UnknownKey]); + this.context.requestTreeshakingPass(); } } } diff --git a/src/ast/nodes/SpreadElement.ts b/src/ast/nodes/SpreadElement.ts index 2071d555793..4e5bee8874c 100644 --- a/src/ast/nodes/SpreadElement.ts +++ b/src/ast/nodes/SpreadElement.ts @@ -30,5 +30,6 @@ export default class SpreadElement extends NodeBase { // Only properties of properties of the argument could become subject to reassignment // This will also reassign the return values of iterators this.argument.deoptimizePath([UnknownKey, UnknownKey]); + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/UnaryExpression.ts b/src/ast/nodes/UnaryExpression.ts index 8ac43fe2a81..4b7f22c2bbf 100644 --- a/src/ast/nodes/UnaryExpression.ts +++ b/src/ast/nodes/UnaryExpression.ts @@ -59,6 +59,7 @@ export default class UnaryExpression extends NodeBase { this.deoptimized = true; if (this.operator === 'delete') { this.argument.deoptimizePath(EMPTY_PATH); + this.context.requestTreeshakingPass(); } } } diff --git a/src/ast/nodes/UpdateExpression.ts b/src/ast/nodes/UpdateExpression.ts index cbf87b438da..06aecaab74f 100644 --- a/src/ast/nodes/UpdateExpression.ts +++ b/src/ast/nodes/UpdateExpression.ts @@ -87,5 +87,6 @@ export default class UpdateExpression extends NodeBase { const variable = this.scope.findVariable(this.argument.name); variable.isReassigned = true; } + this.context.requestTreeshakingPass(); } } diff --git a/src/ast/nodes/YieldExpression.ts b/src/ast/nodes/YieldExpression.ts index efa2a085fe8..bd8cec8f3b1 100644 --- a/src/ast/nodes/YieldExpression.ts +++ b/src/ast/nodes/YieldExpression.ts @@ -30,6 +30,10 @@ export default class YieldExpression extends NodeBase { protected applyDeoptimizations(): void { this.deoptimized = true; - this.argument?.deoptimizePath(UNKNOWN_PATH); + const { argument } = this; + if (argument) { + argument.deoptimizePath(UNKNOWN_PATH); + this.context.requestTreeshakingPass(); + } } } diff --git a/test/function/samples/deoptimize-request-treeshaking-pass/_config.js b/test/function/samples/deoptimize-request-treeshaking-pass/_config.js new file mode 100644 index 00000000000..a9c19945001 --- /dev/null +++ b/test/function/samples/deoptimize-request-treeshaking-pass/_config.js @@ -0,0 +1,10 @@ +const assert = require('assert'); +const result = { value: 0 }; + +module.exports = { + description: 'makes sure to request additional passes when a variable is deoptimized', + context: { result }, + exports() { + assert.strictEqual(result.value, 2); + } +}; diff --git a/test/function/samples/deoptimize-request-treeshaking-pass/main.js b/test/function/samples/deoptimize-request-treeshaking-pass/main.js new file mode 100644 index 00000000000..e8c93aad9eb --- /dev/null +++ b/test/function/samples/deoptimize-request-treeshaking-pass/main.js @@ -0,0 +1,17 @@ +function heisenbug() { + var a = false; + function f(b) { + if (a) a === b.c ? result.value++ : result.value--; + a = b.c; + } + function g() {} + function h() { + f({ + c: g + }); + } + h(); + h(); +} +heisenbug(); +heisenbug();