Skip to content
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

Avoid memory issues with hoisted variables #4183

Merged
merged 1 commit into from Jul 21, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/ast/nodes/Identifier.ts
Expand Up @@ -9,7 +9,6 @@ import { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import { NodeEvent } from '../NodeEvents';
import FunctionScope from '../scopes/FunctionScope';
import { EMPTY_PATH, ObjectPath, PathTracker } from '../utils/PathTracker';
import { UNDEFINED_EXPRESSION } from '../values';
import GlobalVariable from '../variables/GlobalVariable';
import LocalVariable from '../variables/LocalVariable';
import Variable from '../variables/Variable';
Expand Down Expand Up @@ -60,7 +59,7 @@ export default class Identifier extends NodeBase implements PatternNode {
variable = this.scope.addDeclaration(this, this.context, init, true);
if (treeshake && treeshake.correctVarValueBeforeDeclaration) {
// Necessary to make sure the init is deoptimized. We cannot call deoptimizePath here.
this.scope.addDeclaration(this, this.context, UNDEFINED_EXPRESSION, true);
variable.markInitializersForDeoptimization();
}
break;
case 'function':
Expand Down
6 changes: 3 additions & 3 deletions src/ast/scopes/BlockScope.ts
@@ -1,7 +1,6 @@
import { AstContext } from '../../Module';
import Identifier from '../nodes/Identifier';
import { ExpressionEntity } from '../nodes/shared/Expression';
import { UNDEFINED_EXPRESSION } from '../values';
import LocalVariable from '../variables/LocalVariable';
import ChildScope from './ChildScope';

Expand All @@ -13,10 +12,11 @@ export default class BlockScope extends ChildScope {
isHoisted: boolean
): LocalVariable {
if (isHoisted) {
this.parent.addDeclaration(identifier, context, init, isHoisted);
const variable = this.parent.addDeclaration(identifier, context, init, isHoisted);
// Necessary to make sure the init is deoptimized for conditional declarations.
// We cannot call deoptimizePath here.
return this.parent.addDeclaration(identifier, context, UNDEFINED_EXPRESSION, isHoisted);
variable.markInitializersForDeoptimization();
return variable;
} else {
return super.addDeclaration(identifier, context, init, false);
}
Expand Down
19 changes: 12 additions & 7 deletions src/ast/variables/LocalVariable.ts
Expand Up @@ -21,7 +21,6 @@ import Variable from './Variable';
const MAX_PATH_DEPTH = 7;

export default class LocalVariable extends Variable {
additionalInitializers: ExpressionEntity[] | null = null;
calledFromTryStatement = false;
declarations: (Identifier | ExportDefaultDeclaration)[];
init: ExpressionEntity | null;
Expand All @@ -30,6 +29,7 @@ export default class LocalVariable extends Variable {
// Caching and deoptimization:
// We track deoptimization when we do not return something unknown
protected deoptimizationTracker: PathTracker;
private additionalInitializers: ExpressionEntity[] | null = null;
private expressionsToBeDeoptimized: DeoptimizableEntity[] = [];

constructor(
Expand All @@ -47,13 +47,9 @@ export default class LocalVariable extends Variable {

addDeclaration(identifier: Identifier, init: ExpressionEntity | null): void {
this.declarations.push(identifier);
if (this.additionalInitializers === null) {
this.additionalInitializers = this.init === null ? [] : [this.init];
this.init = UNKNOWN_EXPRESSION;
this.isReassigned = true;
}
const additionalInitializers = this.markInitializersForDeoptimization();
if (init !== null) {
this.additionalInitializers.push(init);
additionalInitializers.push(init);
}
}

Expand Down Expand Up @@ -212,4 +208,13 @@ export default class LocalVariable extends Variable {
markCalledFromTryStatement(): void {
this.calledFromTryStatement = true;
}

markInitializersForDeoptimization(): ExpressionEntity[] {
if (this.additionalInitializers === null) {
this.additionalInitializers = this.init === null ? [] : [this.init];
this.init = UNKNOWN_EXPRESSION;
this.isReassigned = true;
}
return this.additionalInitializers;
}
}
3 changes: 3 additions & 0 deletions test/form/samples/deep-switch-declarations/_config.js
@@ -0,0 +1,3 @@
module.exports = {
description: 'handles hoisted declarations in deeply nested switch statements'
};
32 changes: 32 additions & 0 deletions test/form/samples/deep-switch-declarations/_expected.js
@@ -0,0 +1,32 @@
switch(x){ default: var x=1;
switch(x){ default: var x=2;
switch(x){ default: var x=3;
switch(x){ default: var x=4;
switch(x){ default: var x=5;
switch(x){ default: var x=6;
switch(x){ default: var x=7;
switch(x){ default: var x=8;
switch(x){ default: var x=9;
switch(x){ default: var x=10;
switch(x){ default: var x=11;
switch(x){ default: var x=12;
switch(x){ default: var x=13;
switch(x){ default: var x=14;
switch(x){ default: var x=15;
switch(x){ default: var x=16;
switch(x){ default: var x=17;
switch(x){ default: var x=18;
switch(x){ default: var x=19;
switch(x){ default: var x=20;
switch(x){ default: var x=21;
switch(x){ default: var x=22;
switch(x){ default: var x=23;
switch(x){ default: var x=24;
switch(x){ default: var x=25;
switch(x){ default: var x=26;
switch(x){ default: var x=27;
switch(x){ default: var x=28;
switch(x){ default: var x=29;
switch(x){ default: var x=30;
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
console.log(x);
32 changes: 32 additions & 0 deletions test/form/samples/deep-switch-declarations/main.js
@@ -0,0 +1,32 @@
switch(x){ default: var x=1;
switch(x){ default: var x=2;
switch(x){ default: var x=3;
switch(x){ default: var x=4;
switch(x){ default: var x=5;
switch(x){ default: var x=6;
switch(x){ default: var x=7;
switch(x){ default: var x=8;
switch(x){ default: var x=9;
switch(x){ default: var x=10;
switch(x){ default: var x=11;
switch(x){ default: var x=12;
switch(x){ default: var x=13;
switch(x){ default: var x=14;
switch(x){ default: var x=15;
switch(x){ default: var x=16;
switch(x){ default: var x=17;
switch(x){ default: var x=18;
switch(x){ default: var x=19;
switch(x){ default: var x=20;
switch(x){ default: var x=21;
switch(x){ default: var x=22;
switch(x){ default: var x=23;
switch(x){ default: var x=24;
switch(x){ default: var x=25;
switch(x){ default: var x=26;
switch(x){ default: var x=27;
switch(x){ default: var x=28;
switch(x){ default: var x=29;
switch(x){ default: var x=30;
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
console.log(x);