Skip to content

Commit

Permalink
Always use a new inclusion context when including declarations of var…
Browse files Browse the repository at this point in the history
…iables, always inlcude labels when not treeshaking, resolves #3473 (#3492)
  • Loading branch information
lukastaegert committed Apr 9, 2020
1 parent 027e1e5 commit 302c322
Show file tree
Hide file tree
Showing 18 changed files with 80 additions and 42 deletions.
4 changes: 1 addition & 3 deletions src/Chunk.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import MagicString, { Bundle as MagicStringBundle, SourceMap } from 'magic-string';
import { relative } from '../browser/path';
import { createInclusionContext } from './ast/ExecutionContext';
import ExportDefaultDeclaration from './ast/nodes/ExportDefaultDeclaration';
import FunctionDeclaration from './ast/nodes/FunctionDeclaration';
import { UNDEFINED_EXPRESSION } from './ast/values';
Expand Down Expand Up @@ -1051,10 +1050,9 @@ export default class Chunk {
}
}
}
const context = createInclusionContext();
for (const { node, resolution } of module.dynamicImports) {
if (node.included && resolution instanceof Module && resolution.chunk === this)
resolution.getOrCreateNamespace().include(context);
resolution.getOrCreateNamespace().include();
}
}
}
23 changes: 9 additions & 14 deletions src/Module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import * as acorn from 'acorn';
import { locate } from 'locate-character';
import MagicString from 'magic-string';
import extractAssignedNames from 'rollup-pluginutils/src/extractAssignedNames';
import {
createHasEffectsContext,
createInclusionContext,
InclusionContext
} from './ast/ExecutionContext';
import { createHasEffectsContext, createInclusionContext } from './ast/ExecutionContext';
import ExportAllDeclaration from './ast/nodes/ExportAllDeclaration';
import ExportDefaultDeclaration from './ast/nodes/ExportDefaultDeclaration';
import ExportNamedDeclaration from './ast/nodes/ExportNamedDeclaration';
Expand Down Expand Up @@ -100,9 +96,9 @@ export interface AstContext {
getModuleName: () => string;
getReexports: () => string[];
importDescriptions: { [name: string]: ImportDescription };
includeAndGetAdditionalMergedNamespaces: (context: InclusionContext) => Variable[];
includeAndGetAdditionalMergedNamespaces: () => Variable[];
includeDynamicImport: (node: ImportExpression) => void;
includeVariable: (context: InclusionContext, variable: Variable) => void;
includeVariable: (variable: Variable) => void;
magicString: MagicString;
module: Module; // not to be used for tree-shaking
moduleContext: string;
Expand Down Expand Up @@ -565,12 +561,11 @@ export default class Module {
markModuleAndImpureDependenciesAsExecuted(this);
}

const context = createInclusionContext();
for (const exportName of this.getExports()) {
const variable = this.getVariableForExportName(exportName);
variable.deoptimizePath(UNKNOWN_PATH);
if (!variable.included) {
variable.include(context);
variable.include();
this.graph.needsTreeshakingPass = true;
}
}
Expand All @@ -579,7 +574,7 @@ export default class Module {
const variable = this.getVariableForExportName(name);
variable.deoptimizePath(UNKNOWN_PATH);
if (!variable.included) {
variable.include(context);
variable.include();
this.graph.needsTreeshakingPass = true;
}
if (variable instanceof ExternalVariable) {
Expand Down Expand Up @@ -905,7 +900,7 @@ export default class Module {
}
}

private includeAndGetAdditionalMergedNamespaces(context: InclusionContext): Variable[] {
private includeAndGetAdditionalMergedNamespaces(): Variable[] {
const mergedNamespaces: Variable[] = [];
for (const module of this.exportAllModules) {
if (module instanceof ExternalModule) {
Expand All @@ -915,7 +910,7 @@ export default class Module {
mergedNamespaces.push(externalVariable);
} else if (module.syntheticNamedExports) {
const syntheticNamespace = module.getDefaultExport();
syntheticNamespace.include(context);
syntheticNamespace.include();
this.imports.add(syntheticNamespace);
mergedNamespaces.push(syntheticNamespace);
}
Expand All @@ -933,10 +928,10 @@ export default class Module {
}
}

private includeVariable(context: InclusionContext, variable: Variable) {
private includeVariable(variable: Variable) {
const variableModule = variable.module;
if (!variable.included) {
variable.include(context);
variable.include();
this.graph.needsTreeshakingPass = true;
}
if (variableModule && variableModule !== this) {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/BreakStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class BreakStatement extends StatementBase {
include(context: InclusionContext) {
this.included = true;
if (this.label) {
this.label.include(context);
this.label.include();
context.includedLabels.add(this.label.name);
}
context.brokenFlow = this.label ? BROKEN_FLOW_ERROR_RETURN_LABEL : BROKEN_FLOW_BREAK_CONTINUE;
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/ContinueStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class ContinueStatement extends StatementBase {
include(context: InclusionContext) {
this.included = true;
if (this.label) {
this.label.include(context);
this.label.include();
context.includedLabels.add(this.label.name);
}
context.brokenFlow = this.label ? BROKEN_FLOW_ERROR_RETURN_LABEL : BROKEN_FLOW_BREAK_CONTINUE;
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/ExportDefaultDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default class ExportDefaultDeclaration extends NodeBase {
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) {
super.include(context, includeChildrenRecursively);
if (includeChildrenRecursively) {
this.context.includeVariable(context, this.variable);
this.context.includeVariable(this.variable);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/ast/nodes/Identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ export default class Identifier extends NodeBase implements PatternNode {
return !this.variable || this.variable.hasEffectsWhenCalledAtPath(path, callOptions, context);
}

include(context: InclusionContext) {
include() {
if (!this.included) {
this.included = true;
if (this.variable !== null) {
this.context.includeVariable(context, this.variable);
this.context.includeVariable(this.variable);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/ast/nodes/LabeledStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export default class LabeledStatement extends StatementBase {
this.included = true;
const brokenFlow = context.brokenFlow;
this.body.include(context, includeChildrenRecursively);
if (context.includedLabels.has(this.label.name)) {
this.label.include(context);
if (includeChildrenRecursively || context.includedLabels.has(this.label.name)) {
this.label.include();
context.includedLabels.delete(this.label.name);
context.brokenFlow = brokenFlow;
}
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/MemberExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export default class MemberExpression extends NodeBase implements DeoptimizableE
if (!this.included) {
this.included = true;
if (this.variable !== null) {
this.context.includeVariable(context, this.variable);
this.context.includeVariable(this.variable);
}
}
this.object.include(context, includeChildrenRecursively);
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/shared/FunctionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default class FunctionNode extends NodeBase {

include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) {
this.included = true;
if (this.id) this.id.include(context);
if (this.id) this.id.include();
const hasArguments = this.scope.argumentsVariable.included;
for (const param of this.params) {
if (!(param instanceof Identifier) || hasArguments) {
Expand Down
6 changes: 3 additions & 3 deletions src/ast/variables/LocalVariable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Module, { AstContext } from '../../Module';
import { markModuleAndImpureDependenciesAsExecuted } from '../../utils/traverseStaticDependencies';
import { CallOptions } from '../CallOptions';
import { DeoptimizableEntity } from '../DeoptimizableEntity';
import { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import { createInclusionContext, HasEffectsContext, InclusionContext } from '../ExecutionContext';
import ExportDefaultDeclaration from '../nodes/ExportDefaultDeclaration';
import Identifier from '../nodes/Identifier';
import * as NodeType from '../nodes/NodeType';
Expand Down Expand Up @@ -156,15 +156,15 @@ export default class LocalVariable extends Variable {
return (this.init && this.init.hasEffectsWhenCalledAtPath(path, callOptions, context))!;
}

include(context: InclusionContext) {
include() {
if (!this.included) {
this.included = true;
if (!this.module.isExecuted) {
markModuleAndImpureDependenciesAsExecuted(this.module);
}
for (const declaration of this.declarations) {
// If node is a default export, it can save a tree-shaking run to include the full declaration now
if (!declaration.included) declaration.include(context, false);
if (!declaration.included) declaration.include(createInclusionContext(), false);
let node = declaration.parent as Node;
while (!node.included) {
// We do not want to properly include parents in case they are part of a dead branch
Expand Down
9 changes: 4 additions & 5 deletions src/ast/variables/NamespaceVariable.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Module, { AstContext } from '../../Module';
import { RenderOptions } from '../../utils/renderHelpers';
import { RESERVED_NAMES } from '../../utils/reservedNames';
import { InclusionContext } from '../ExecutionContext';
import Identifier from '../nodes/Identifier';
import { UNKNOWN_PATH } from '../utils/PathTracker';
import Variable from './Variable';
Expand Down Expand Up @@ -39,7 +38,7 @@ export default class NamespaceVariable extends Variable {
}
}

include(context: InclusionContext) {
include() {
if (!this.included) {
this.included = true;
for (const identifier of this.references) {
Expand All @@ -48,13 +47,13 @@ export default class NamespaceVariable extends Variable {
break;
}
}
this.mergedNamespaces = this.context.includeAndGetAdditionalMergedNamespaces(context);
this.mergedNamespaces = this.context.includeAndGetAdditionalMergedNamespaces();
if (this.context.preserveModules) {
for (const memberName of Object.keys(this.memberVariables))
this.memberVariables[memberName].include(context);
this.memberVariables[memberName].include();
} else {
for (const memberName of Object.keys(this.memberVariables))
this.context.includeVariable(context, this.memberVariables[memberName]);
this.context.includeVariable(this.memberVariables[memberName]);
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/ast/variables/SyntheticNamedExportVariable.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Module, { AstContext } from '../../Module';
import { InclusionContext } from '../ExecutionContext';
import ExportDefaultVariable from './ExportDefaultVariable';
import Variable from './Variable';

Expand All @@ -25,10 +24,10 @@ export default class SyntheticNamedExportVariable extends Variable {
return this.defaultVariable.getOriginalVariable();
}

include(context: InclusionContext) {
include() {
if (!this.included) {
this.included = true;
this.context.includeVariable(context, this.defaultVariable);
this.context.includeVariable(this.defaultVariable);
}
}
}
Expand Down
6 changes: 1 addition & 5 deletions src/ast/variables/Variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default class Variable implements ExpressionEntity {
* previously.
* Once a variable is included, it should take care all its declarations are included.
*/
include(_context: InclusionContext) {
include() {
this.included = true;
}

Expand All @@ -102,8 +102,4 @@ export default class Variable implements ExpressionEntity {
setSafeName(name: string | null) {
this.renderName = name;
}

toString() {
return this.name;
}
}
7 changes: 7 additions & 0 deletions test/form/samples/no-treeshake-include-labels/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
description: 'always includes labels when tree-shaking is turned off (#3473)',
expectedWarnings: ['CIRCULAR_DEPENDENCY'],
options: {
treeshake: false
}
};
20 changes: 20 additions & 0 deletions test/form/samples/no-treeshake-include-labels/_expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function foo() {
loop: while (unknown) {
if (unknown) {
break loop;
}
bar();
}
}

function bar() {
loop: while (unknown) {
if (unknown) {
break loop;
}
foo();
}
}

unused: {
}
10 changes: 10 additions & 0 deletions test/form/samples/no-treeshake-include-labels/dep1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import foo from './dep2.js';

export default function bar() {
loop: while (unknown) {
if (unknown) {
break loop;
}
foo();
}
}
10 changes: 10 additions & 0 deletions test/form/samples/no-treeshake-include-labels/dep2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import bar from './dep1.js';

export default function foo() {
loop: while (unknown) {
if (unknown) {
break loop;
}
bar();
}
}
4 changes: 4 additions & 0 deletions test/form/samples/no-treeshake-include-labels/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import './dep1.js';

unused: {
}

0 comments on commit 302c322

Please sign in to comment.