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

Deoptimize parameter defaults when referenced from object/array/class literals #4518

Merged
merged 1 commit into from May 31, 2022
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
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