Skip to content

Commit

Permalink
Deoptimize parameter defaults when referenced from object/array/class…
Browse files Browse the repository at this point in the history
… literals (rollup#4518)
  • Loading branch information
lukastaegert authored and pos777 committed Jun 18, 2022
1 parent 7d74b51 commit ee888ab
Show file tree
Hide file tree
Showing 47 changed files with 122 additions and 24 deletions.
9 changes: 6 additions & 3 deletions src/ast/nodes/ArrayExpression.ts
Expand Up @@ -19,7 +19,6 @@ import { ObjectEntity, type ObjectProperty } from './shared/ObjectEntity';
export default class ArrayExpression extends NodeBase {
declare elements: readonly (ExpressionNode | SpreadElement | null)[];
declare type: NodeType.tArrayExpression;
protected deoptimized = false;
private objectEntity: ObjectEntity | null = null;

deoptimizePath(path: ObjectPath): void {
Expand Down Expand Up @@ -83,10 +82,14 @@ export default class ArrayExpression extends NodeBase {
let hasSpread = false;
for (let index = 0; index < this.elements.length; index++) {
const element = this.elements[index];
if (hasSpread || element instanceof SpreadElement) {
if (element) {
if (element) {
if (hasSpread || element instanceof SpreadElement) {
hasSpread = true;
// This also deoptimizes parameter defaults
element.deoptimizePath(UNKNOWN_PATH);
} else {
// We do not track parameter defaults in arrays
element.deoptimizeCallParameters();
}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/AssignmentExpression.ts
Expand Up @@ -43,7 +43,6 @@ export default class AssignmentExpression extends NodeBase {
| '**=';
declare right: ExpressionNode;
declare type: NodeType.tAssignmentExpression;
protected deoptimized = false;

hasEffects(context: HasEffectsContext): boolean {
if (!this.deoptimized) this.applyDeoptimizations();
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/AssignmentPattern.ts
Expand Up @@ -15,7 +15,6 @@ export default class AssignmentPattern extends NodeBase implements PatternNode {
declare left: PatternNode;
declare right: ExpressionNode;
declare type: NodeType.tAssignmentPattern;
protected deoptimized = false;

addExportedVariables(
variables: readonly Variable[],
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/AwaitExpression.ts
Expand Up @@ -7,7 +7,6 @@ import { type ExpressionNode, type IncludeChildren, type Node, NodeBase } from '
export default class AwaitExpression extends NodeBase {
declare argument: ExpressionNode;
declare type: NodeType.tAwaitExpression;
protected deoptimized = false;

hasEffects(): boolean {
if (!this.deoptimized) this.applyDeoptimizations();
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/ClassBody.ts
Expand Up @@ -37,4 +37,6 @@ export default class ClassBody extends NodeBase {
}
super.parseNode(esTreeNode);
}

protected applyDeoptimizations() {}
}
2 changes: 2 additions & 0 deletions src/ast/nodes/ExportAllDeclaration.ts
Expand Up @@ -22,6 +22,8 @@ export default class ExportAllDeclaration extends NodeBase {
render(code: MagicString, _options: RenderOptions, nodeRenderOptions?: NodeRenderOptions): void {
code.remove(nodeRenderOptions!.start!, nodeRenderOptions!.end!);
}

protected applyDeoptimizations() {}
}

ExportAllDeclaration.prototype.needsBoundaries = true;
2 changes: 2 additions & 0 deletions src/ast/nodes/ExportDefaultDeclaration.ts
Expand Up @@ -109,6 +109,8 @@ export default class ExportDefaultDeclaration extends NodeBase {
this.declaration.render(code, options);
}

protected applyDeoptimizations() {}

private renderNamedDeclaration(
code: MagicString,
declarationStart: number,
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/ExportNamedDeclaration.ts
Expand Up @@ -38,6 +38,8 @@ export default class ExportNamedDeclaration extends NodeBase {
(this.declaration as Node).render(code, options, { end, start });
}
}

protected applyDeoptimizations() {}
}

ExportNamedDeclaration.prototype.needsBoundaries = true;
2 changes: 2 additions & 0 deletions src/ast/nodes/ExportSpecifier.ts
Expand Up @@ -6,4 +6,6 @@ export default class ExportSpecifier extends NodeBase {
declare exported: Identifier;
declare local: Identifier;
declare type: NodeType.tExportSpecifier;

protected applyDeoptimizations() {}
}
2 changes: 2 additions & 0 deletions src/ast/nodes/ExpressionStatement.ts
Expand Up @@ -36,4 +36,6 @@ export default class ExpressionStatement extends StatementBase {

return super.shouldBeIncluded(context);
}

protected applyDeoptimizations() {}
}
1 change: 0 additions & 1 deletion src/ast/nodes/ForInStatement.ts
Expand Up @@ -19,7 +19,6 @@ export default class ForInStatement extends StatementBase {
declare left: VariableDeclaration | PatternNode;
declare right: ExpressionNode;
declare type: NodeType.tForInStatement;
protected deoptimized = false;

createScope(parentScope: Scope): void {
this.scope = new BlockScope(parentScope);
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/ForOfStatement.ts
Expand Up @@ -20,7 +20,6 @@ export default class ForOfStatement extends StatementBase {
declare left: VariableDeclaration | PatternNode;
declare right: ExpressionNode;
declare type: NodeType.tForOfStatement;
protected deoptimized = false;

createScope(parentScope: Scope): void {
this.scope = new BlockScope(parentScope);
Expand Down
9 changes: 7 additions & 2 deletions src/ast/nodes/Identifier.ts
Expand Up @@ -36,7 +36,6 @@ export default class Identifier extends NodeBase implements PatternNode {
declare name: string;
declare type: NodeType.tIdentifier;
variable: Variable | null = null;
protected deoptimized = false;
private isTDZAccess: boolean | null = null;

addExportedVariables(
Expand Down Expand Up @@ -87,11 +86,17 @@ export default class Identifier extends NodeBase implements PatternNode {
return [(this.variable = variable)];
}

deoptimizeCallParameters() {
this.variable!.deoptimizeCallParameters();
}

deoptimizePath(path: ObjectPath): void {
if (path.length === 0 && !this.scope.contains(this.name)) {
this.disallowImportReassignment();
}
this.variable!.deoptimizePath(path);
// We keep conditional chaining because an unknown Node could have an
// Identifier as property that might be deoptimized by default
this.variable?.deoptimizePath(path);
}

deoptimizeThisOnEventAtPath(
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/IfStatement.ts
Expand Up @@ -129,6 +129,8 @@ export default class IfStatement extends StatementBase implements DeoptimizableE
this.renderHoistedDeclarations(hoistedDeclarations, code, getPropertyAccess);
}

protected applyDeoptimizations() {}

private getTestValue(): LiteralValueOrUnknown {
if (this.testValue === unset) {
return (this.testValue = this.test.getLiteralValueAtPath(
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/ImportDeclaration.ts
Expand Up @@ -27,6 +27,8 @@ export default class ImportDeclaration extends NodeBase {
render(code: MagicString, _options: RenderOptions, nodeRenderOptions?: NodeRenderOptions): void {
code.remove(nodeRenderOptions!.start!, nodeRenderOptions!.end!);
}

protected applyDeoptimizations() {}
}

ImportDeclaration.prototype.needsBoundaries = true;
2 changes: 2 additions & 0 deletions src/ast/nodes/ImportDefaultSpecifier.ts
Expand Up @@ -5,4 +5,6 @@ import { NodeBase } from './shared/Node';
export default class ImportDefaultSpecifier extends NodeBase {
declare local: Identifier;
declare type: NodeType.tImportDefaultSpecifier;

protected applyDeoptimizations() {}
}
2 changes: 2 additions & 0 deletions src/ast/nodes/ImportExpression.ts
Expand Up @@ -124,6 +124,8 @@ export default class ImportExpression extends NodeBase {
this.inlineNamespace = inlineNamespace;
}

protected applyDeoptimizations() {}

private getDynamicImportMechanismAndHelper(
resolution: Module | ExternalModule | string | null,
exportMode: 'none' | 'named' | 'default' | 'external',
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/ImportNamespaceSpecifier.ts
Expand Up @@ -5,4 +5,6 @@ import { NodeBase } from './shared/Node';
export default class ImportNamespaceSpecifier extends NodeBase {
declare local: Identifier;
declare type: NodeType.tImportNamespaceSpecifier;

protected applyDeoptimizations() {}
}
2 changes: 2 additions & 0 deletions src/ast/nodes/ImportSpecifier.ts
Expand Up @@ -6,4 +6,6 @@ export default class ImportSpecifier extends NodeBase {
declare imported: Identifier;
declare local: Identifier;
declare type: NodeType.tImportSpecifier;

protected applyDeoptimizations() {}
}
1 change: 0 additions & 1 deletion src/ast/nodes/MemberExpression.ts
Expand Up @@ -89,7 +89,6 @@ export default class MemberExpression extends NodeBase implements DeoptimizableE
declare propertyKey: ObjectPathKey | null;
declare type: NodeType.tMemberExpression;
variable: Variable | null = null;
protected deoptimized = false;
private bound = false;
private expressionsToBeDeoptimized: DeoptimizableEntity[] = [];
private replacement: string | null = null;
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/MethodDefinition.ts
Expand Up @@ -10,4 +10,6 @@ export default class MethodDefinition extends MethodBase {
declare static: boolean;
declare type: NodeType.tMethodDefinition;
declare value: FunctionExpression;

protected applyDeoptimizations() {}
}
1 change: 0 additions & 1 deletion src/ast/nodes/NewExpression.ts
Expand Up @@ -10,7 +10,6 @@ export default class NewExpression extends NodeBase {
declare arguments: ExpressionNode[];
declare callee: ExpressionNode;
declare type: NodeType.tNewExpression;
protected deoptimized = false;
private declare callOptions: CallOptions;

hasEffects(context: HasEffectsContext): boolean {
Expand Down
11 changes: 10 additions & 1 deletion src/ast/nodes/ObjectExpression.ts
Expand Up @@ -15,7 +15,7 @@ import {
import Identifier from './Identifier';
import Literal from './Literal';
import * as NodeType from './NodeType';
import type Property from './Property';
import Property from './Property';
import SpreadElement from './SpreadElement';
import { type ExpressionEntity, type LiteralValueOrUnknown } from './shared/Expression';
import { NodeBase } from './shared/Node';
Expand Down Expand Up @@ -102,6 +102,15 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE
}
}

protected applyDeoptimizations() {
this.deoptimized = true;
for (const property of this.properties) {
if (property instanceof Property) {
property.value.deoptimizeCallParameters();
}
}
}

private getObjectEntity(): ObjectEntity {
if (this.objectEntity !== null) {
return this.objectEntity;
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/Program.ts
Expand Up @@ -38,4 +38,6 @@ export default class Program extends NodeBase {
super.render(code, options);
}
}

protected applyDeoptimizations() {}
}
1 change: 0 additions & 1 deletion src/ast/nodes/Property.ts
Expand Up @@ -16,7 +16,6 @@ export default class Property extends MethodBase implements PatternNode {
declare method: boolean;
declare shorthand: boolean;
declare type: NodeType.tProperty;
protected deoptimized = false;
private declarationInit: ExpressionEntity | null = null;

declare(kind: string, init: ExpressionEntity): LocalVariable[] {
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/PropertyDefinition.ts
Expand Up @@ -73,4 +73,6 @@ export default class PropertyDefinition extends NodeBase {
): boolean {
return !this.value || this.value.hasEffectsWhenCalledAtPath(path, callOptions, context);
}

protected applyDeoptimizations() {}
}
1 change: 0 additions & 1 deletion src/ast/nodes/RestElement.ts
Expand Up @@ -10,7 +10,6 @@ import type { PatternNode } from './shared/Pattern';
export default class RestElement extends NodeBase implements PatternNode {
declare argument: PatternNode;
declare type: NodeType.tRestElement;
protected deoptimized = false;
private declarationInit: ExpressionEntity | null = null;

addExportedVariables(
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/SpreadElement.ts
Expand Up @@ -9,7 +9,6 @@ import { type ExpressionNode, NodeBase } from './shared/Node';
export default class SpreadElement extends NodeBase {
declare argument: ExpressionNode;
declare type: NodeType.tSpreadElement;
protected deoptimized = false;

deoptimizeThisOnEventAtPath(
event: NodeEvent,
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/UnaryExpression.ts
Expand Up @@ -24,7 +24,6 @@ export default class UnaryExpression extends NodeBase {
declare operator: '!' | '+' | '-' | 'delete' | 'typeof' | 'void' | '~';
declare prefix: boolean;
declare type: NodeType.tUnaryExpression;
protected deoptimized = false;

getLiteralValueAtPath(
path: ObjectPath,
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/UpdateExpression.ts
Expand Up @@ -16,7 +16,6 @@ export default class UpdateExpression extends NodeBase {
declare operator: '++' | '--';
declare prefix: boolean;
declare type: NodeType.tUpdateExpression;
protected deoptimized = false;

hasEffects(context: HasEffectsContext): boolean {
if (!this.deoptimized) this.applyDeoptimizations();
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/VariableDeclaration.ts
Expand Up @@ -96,6 +96,8 @@ export default class VariableDeclaration extends NodeBase {
}
}

protected applyDeoptimizations() {}

private renderDeclarationEnd(
code: MagicString,
separatorString: string,
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/VariableDeclarator.ts
Expand Up @@ -69,4 +69,6 @@ export default class VariableDeclarator extends NodeBase {
code.appendLeft(this.end, `${_}=${_}void 0`);
}
}

protected applyDeoptimizations() {}
}
1 change: 0 additions & 1 deletion src/ast/nodes/YieldExpression.ts
Expand Up @@ -8,7 +8,6 @@ export default class YieldExpression extends NodeBase {
declare argument: ExpressionNode | null;
declare delegate: boolean;
declare type: NodeType.tYieldExpression;
protected deoptimized = false;

hasEffects(context: HasEffectsContext): boolean {
if (!this.deoptimized) this.applyDeoptimizations();
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/shared/CallExpressionBase.ts
Expand Up @@ -13,7 +13,6 @@ import { NodeBase } from './Node';

export default abstract class CallExpressionBase extends NodeBase implements DeoptimizableEntity {
protected declare callOptions: CallOptions;
protected deoptimized = false;
protected returnExpression: ExpressionEntity | null = null;
private readonly deoptimizableDependentExpressions: DeoptimizableEntity[] = [];
private readonly expressionsToBeDeoptimized = new Set<ExpressionEntity>();
Expand Down
5 changes: 4 additions & 1 deletion src/ast/nodes/shared/ClassNode.ts
Expand Up @@ -16,6 +16,7 @@ import type ClassBody from '../ClassBody';
import Identifier from '../Identifier';
import type Literal from '../Literal';
import MethodDefinition from '../MethodDefinition';
import PropertyDefinition from '../PropertyDefinition';
import { type ExpressionEntity, type LiteralValueOrUnknown } from './Expression';
import { type ExpressionNode, type IncludeChildren, NodeBase } from './Node';
import { ObjectEntity, type ObjectProperty } from './ObjectEntity';
Expand All @@ -26,7 +27,6 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity {
declare body: ClassBody;
declare id: Identifier | null;
declare superClass: ExpressionNode | null;
protected deoptimized = false;
private declare classConstructor: MethodDefinition | null;
private objectEntity: ObjectEntity | null = null;

Expand Down Expand Up @@ -150,8 +150,11 @@ export default class ClassNode extends NodeBase implements DeoptimizableEntity {
) {
// Calls to methods are not tracked, ensure that the return value is deoptimized
definition.deoptimizePath(UNKNOWN_PATH);
} else if (definition instanceof PropertyDefinition) {
definition.value?.deoptimizeCallParameters();
}
}
this.superClass?.deoptimizeCallParameters();
this.context.requestTreeshakingPass();
}

Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/shared/Expression.ts
Expand Up @@ -24,6 +24,8 @@ export interface InclusionOptions {
export class ExpressionEntity implements WritableEntity {
included = false;

deoptimizeCallParameters(): void {}

deoptimizePath(_path: ObjectPath): void {}

deoptimizeThisOnEventAtPath(
Expand Down
4 changes: 4 additions & 0 deletions src/ast/nodes/shared/FunctionBase.ts
Expand Up @@ -54,6 +54,10 @@ export default abstract class FunctionBase extends NodeBase implements Deoptimiz
this.forceIncludeParameters = true;
}

deoptimizeCallParameters() {
this.forceIncludeParameters = true;
}

deoptimizePath(path: ObjectPath): void {
this.getObjectEntity().deoptimizePath(path);
if (path.length === 1 && path[0] === UnknownKey) {
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/shared/MethodBase.ts
Expand Up @@ -116,6 +116,8 @@ export default class MethodBase extends NodeBase implements DeoptimizableEntity
return this.getAccessedValue().hasEffectsWhenCalledAtPath(path, callOptions, context);
}

protected applyDeoptimizations() {}

protected getAccessedValue(): ExpressionEntity {
if (this.accessedValue === null) {
if (this.kind === 'get') {
Expand Down

0 comments on commit ee888ab

Please sign in to comment.