Skip to content

Commit

Permalink
Make sure the initializer of hoisted variables is deoptimized (#4149)
Browse files Browse the repository at this point in the history
* Make sure the initializer of hoisted variables is deoptimized

* Also deoptimize var initializers when force deoptimizing via flag

* Do not check types on Windows
  • Loading branch information
lukastaegert committed Jun 25, 2021
1 parent 6eaecf3 commit 6260def
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Expand Up @@ -85,6 +85,6 @@ jobs:
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Run tests
run: npm test
run: npm run ci:test:only
env:
CI: true
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -11,7 +11,7 @@
"scripts": {
"build": "shx rm -rf dist && git rev-parse HEAD > .commithash && rollup --config rollup.config.ts --configPlugin typescript && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup",
"build:cjs": "shx rm -rf dist && rollup --config rollup.config.ts --configPlugin typescript --configTest && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup",
"build:bootstrap": "dist/bin/rollup --config rollup.config.ts --configPlugin typescript && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup",
"build:bootstrap": "node dist/bin/rollup --config rollup.config.ts --configPlugin typescript && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup",
"ci:lint": "npm run lint:nofix",
"ci:test": "npm run build:cjs && npm run build:bootstrap && npm run test:all",
"ci:test:only": "npm run build:cjs && npm run build:bootstrap && npm run test:only",
Expand Down
14 changes: 7 additions & 7 deletions src/ast/nodes/Identifier.ts
Expand Up @@ -9,12 +9,13 @@ 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';
import * as NodeType from './NodeType';
import SpreadElement from './SpreadElement';
import { ExpressionEntity, LiteralValueOrUnknown, UNKNOWN_EXPRESSION } from './shared/Expression';
import { ExpressionEntity, LiteralValueOrUnknown } from './shared/Expression';
import { ExpressionNode, NodeBase } from './shared/Node';
import { PatternNode } from './shared/Pattern';

Expand Down Expand Up @@ -48,12 +49,11 @@ export default class Identifier extends NodeBase implements PatternNode {
const { treeshake } = this.context.options;
switch (kind) {
case 'var':
variable = this.scope.addDeclaration(
this,
this.context,
treeshake && treeshake.correctVarValueBeforeDeclaration ? UNKNOWN_EXPRESSION : init,
true
);
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);
}
break;
case 'function':
// in strict mode, functions are only hoisted within a scope but not across block scopes
Expand Down
7 changes: 5 additions & 2 deletions src/ast/scopes/BlockScope.ts
@@ -1,6 +1,7 @@
import { AstContext } from '../../Module';
import Identifier from '../nodes/Identifier';
import { ExpressionEntity, UNKNOWN_EXPRESSION } from '../nodes/shared/Expression';
import { ExpressionEntity } from '../nodes/shared/Expression';
import { UNDEFINED_EXPRESSION } from '../values';
import LocalVariable from '../variables/LocalVariable';
import ChildScope from './ChildScope';

Expand All @@ -12,7 +13,9 @@ export default class BlockScope extends ChildScope {
isHoisted: boolean
): LocalVariable {
if (isHoisted) {
return this.parent.addDeclaration(identifier, context, UNKNOWN_EXPRESSION, isHoisted);
this.parent.addDeclaration(identifier, context, init, isHoisted);
// Necessary to make sure the init is deoptimized. We cannot call deoptimizePath here.
return this.parent.addDeclaration(identifier, context, UNDEFINED_EXPRESSION, isHoisted);
} else {
return super.addDeclaration(identifier, context, init, false);
}
Expand Down
3 changes: 1 addition & 2 deletions src/ast/scopes/CatchScope.ts
Expand Up @@ -16,8 +16,7 @@ export default class CatchScope extends ParameterScope {
existingParameter.addDeclaration(identifier, init);
return existingParameter;
}
// as parameters are handled differently, all remaining declarations are
// hoisted
// as parameters are handled differently, all remaining declarations are hoisted
return this.parent.addDeclaration(identifier, context, init, isHoisted);
}
}
3 changes: 3 additions & 0 deletions test/form/samples/deoptimize-var-in-hoisted-scopes/_config.js
@@ -0,0 +1,3 @@
module.exports = {
description: 'deoptimizes var variables in hoisted scopes'
};
15 changes: 15 additions & 0 deletions test/form/samples/deoptimize-var-in-hoisted-scopes/_expected.js
@@ -0,0 +1,15 @@
const obj1 = { flag: false };
{
var foo = obj1;
foo.flag = true;
}
if (obj1.flag) console.log('retained');

const obj2 = { flag: false };
try {
throw new Error();
} catch {
var foo = obj2;
foo.flag = true;
}
if (obj2.flag) console.log('retained');
15 changes: 15 additions & 0 deletions test/form/samples/deoptimize-var-in-hoisted-scopes/main.js
@@ -0,0 +1,15 @@
const obj1 = { flag: false };
{
var foo = obj1;
foo.flag = true;
}
if (obj1.flag) console.log('retained');

const obj2 = { flag: false };
try {
throw new Error();
} catch {
var foo = obj2;
foo.flag = true;
}
if (obj2.flag) console.log('retained');
@@ -0,0 +1,6 @@
module.exports = {
description: 'adds necessary deoptimizations when using treeshake.correctVarBeforeDeclaration',
options: {
treeshake: { correctVarValueBeforeDeclaration: true }
}
};
@@ -0,0 +1,15 @@
const obj1 = { flag: false };
{
var foo = obj1;
foo.flag = true;
}
if (obj1.flag) console.log('retained');

const obj2 = { flag: false };
try {
throw new Error();
} catch {
var foo = obj2;
foo.flag = true;
}
if (obj2.flag) console.log('retained');
@@ -0,0 +1,7 @@
const obj = { flag: false };
var foo = obj;
foo.flag = true;
assert.ok(obj.flag ? true : false, 'init deoptimization');

assert.ok(bar ? false : true, 'value deoptimization');
var bar = true;

0 comments on commit 6260def

Please sign in to comment.