diff --git a/src/ast/nodes/ArrayExpression.ts b/src/ast/nodes/ArrayExpression.ts index d8c6f9193af..9b9ce995d53 100644 --- a/src/ast/nodes/ArrayExpression.ts +++ b/src/ast/nodes/ArrayExpression.ts @@ -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 { @@ -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(); } } } diff --git a/src/ast/nodes/AssignmentExpression.ts b/src/ast/nodes/AssignmentExpression.ts index b139d1ffe8f..8912b01cdb3 100644 --- a/src/ast/nodes/AssignmentExpression.ts +++ b/src/ast/nodes/AssignmentExpression.ts @@ -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(); diff --git a/src/ast/nodes/AssignmentPattern.ts b/src/ast/nodes/AssignmentPattern.ts index 8575ce56d00..08e0b799910 100644 --- a/src/ast/nodes/AssignmentPattern.ts +++ b/src/ast/nodes/AssignmentPattern.ts @@ -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[], diff --git a/src/ast/nodes/AwaitExpression.ts b/src/ast/nodes/AwaitExpression.ts index 2f27a1b47d4..aafe5995625 100644 --- a/src/ast/nodes/AwaitExpression.ts +++ b/src/ast/nodes/AwaitExpression.ts @@ -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(); diff --git a/src/ast/nodes/ClassBody.ts b/src/ast/nodes/ClassBody.ts index 554518f34b1..21da58a4420 100644 --- a/src/ast/nodes/ClassBody.ts +++ b/src/ast/nodes/ClassBody.ts @@ -37,4 +37,6 @@ export default class ClassBody extends NodeBase { } super.parseNode(esTreeNode); } + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/ExportAllDeclaration.ts b/src/ast/nodes/ExportAllDeclaration.ts index e297cc518e3..d4828620bf7 100644 --- a/src/ast/nodes/ExportAllDeclaration.ts +++ b/src/ast/nodes/ExportAllDeclaration.ts @@ -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; diff --git a/src/ast/nodes/ExportDefaultDeclaration.ts b/src/ast/nodes/ExportDefaultDeclaration.ts index be735a44db9..b0ed6e8f24f 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.ts +++ b/src/ast/nodes/ExportDefaultDeclaration.ts @@ -109,6 +109,8 @@ export default class ExportDefaultDeclaration extends NodeBase { this.declaration.render(code, options); } + protected applyDeoptimizations() {} + private renderNamedDeclaration( code: MagicString, declarationStart: number, diff --git a/src/ast/nodes/ExportNamedDeclaration.ts b/src/ast/nodes/ExportNamedDeclaration.ts index e170fefd27a..2b63e982618 100644 --- a/src/ast/nodes/ExportNamedDeclaration.ts +++ b/src/ast/nodes/ExportNamedDeclaration.ts @@ -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; diff --git a/src/ast/nodes/ExportSpecifier.ts b/src/ast/nodes/ExportSpecifier.ts index 9999512a2de..8c2d1086654 100644 --- a/src/ast/nodes/ExportSpecifier.ts +++ b/src/ast/nodes/ExportSpecifier.ts @@ -6,4 +6,6 @@ export default class ExportSpecifier extends NodeBase { declare exported: Identifier; declare local: Identifier; declare type: NodeType.tExportSpecifier; + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/ExpressionStatement.ts b/src/ast/nodes/ExpressionStatement.ts index f5c141c84d2..e6dc4d6bf67 100644 --- a/src/ast/nodes/ExpressionStatement.ts +++ b/src/ast/nodes/ExpressionStatement.ts @@ -36,4 +36,6 @@ export default class ExpressionStatement extends StatementBase { return super.shouldBeIncluded(context); } + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/ForInStatement.ts b/src/ast/nodes/ForInStatement.ts index 9669720ba8d..46a91d4595f 100644 --- a/src/ast/nodes/ForInStatement.ts +++ b/src/ast/nodes/ForInStatement.ts @@ -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); diff --git a/src/ast/nodes/ForOfStatement.ts b/src/ast/nodes/ForOfStatement.ts index 4ece368c04d..8d35fd4e136 100644 --- a/src/ast/nodes/ForOfStatement.ts +++ b/src/ast/nodes/ForOfStatement.ts @@ -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); diff --git a/src/ast/nodes/Identifier.ts b/src/ast/nodes/Identifier.ts index 1043e5dee46..2ce3511a769 100644 --- a/src/ast/nodes/Identifier.ts +++ b/src/ast/nodes/Identifier.ts @@ -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( @@ -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( diff --git a/src/ast/nodes/IfStatement.ts b/src/ast/nodes/IfStatement.ts index f32fcf102ab..50d792f4d10 100644 --- a/src/ast/nodes/IfStatement.ts +++ b/src/ast/nodes/IfStatement.ts @@ -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( diff --git a/src/ast/nodes/ImportDeclaration.ts b/src/ast/nodes/ImportDeclaration.ts index 3291c84ce2e..9a4d98aff71 100644 --- a/src/ast/nodes/ImportDeclaration.ts +++ b/src/ast/nodes/ImportDeclaration.ts @@ -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; diff --git a/src/ast/nodes/ImportDefaultSpecifier.ts b/src/ast/nodes/ImportDefaultSpecifier.ts index 87c05c9eab0..91079284281 100644 --- a/src/ast/nodes/ImportDefaultSpecifier.ts +++ b/src/ast/nodes/ImportDefaultSpecifier.ts @@ -5,4 +5,6 @@ import { NodeBase } from './shared/Node'; export default class ImportDefaultSpecifier extends NodeBase { declare local: Identifier; declare type: NodeType.tImportDefaultSpecifier; + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/ImportExpression.ts b/src/ast/nodes/ImportExpression.ts index 44a776a93b1..6f9d8ae4e05 100644 --- a/src/ast/nodes/ImportExpression.ts +++ b/src/ast/nodes/ImportExpression.ts @@ -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', diff --git a/src/ast/nodes/ImportNamespaceSpecifier.ts b/src/ast/nodes/ImportNamespaceSpecifier.ts index 0ad0c019765..f4d4d1dd074 100644 --- a/src/ast/nodes/ImportNamespaceSpecifier.ts +++ b/src/ast/nodes/ImportNamespaceSpecifier.ts @@ -5,4 +5,6 @@ import { NodeBase } from './shared/Node'; export default class ImportNamespaceSpecifier extends NodeBase { declare local: Identifier; declare type: NodeType.tImportNamespaceSpecifier; + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/ImportSpecifier.ts b/src/ast/nodes/ImportSpecifier.ts index efab57084cf..723d3244172 100644 --- a/src/ast/nodes/ImportSpecifier.ts +++ b/src/ast/nodes/ImportSpecifier.ts @@ -6,4 +6,6 @@ export default class ImportSpecifier extends NodeBase { declare imported: Identifier; declare local: Identifier; declare type: NodeType.tImportSpecifier; + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/MemberExpression.ts b/src/ast/nodes/MemberExpression.ts index 20b957e5658..d98d548fa30 100644 --- a/src/ast/nodes/MemberExpression.ts +++ b/src/ast/nodes/MemberExpression.ts @@ -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; diff --git a/src/ast/nodes/MethodDefinition.ts b/src/ast/nodes/MethodDefinition.ts index c5bab7b33ec..9b84c7eef5a 100644 --- a/src/ast/nodes/MethodDefinition.ts +++ b/src/ast/nodes/MethodDefinition.ts @@ -10,4 +10,6 @@ export default class MethodDefinition extends MethodBase { declare static: boolean; declare type: NodeType.tMethodDefinition; declare value: FunctionExpression; + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/NewExpression.ts b/src/ast/nodes/NewExpression.ts index 871c82fb5f3..30725efc863 100644 --- a/src/ast/nodes/NewExpression.ts +++ b/src/ast/nodes/NewExpression.ts @@ -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 { diff --git a/src/ast/nodes/ObjectExpression.ts b/src/ast/nodes/ObjectExpression.ts index a0746190271..034281064e7 100644 --- a/src/ast/nodes/ObjectExpression.ts +++ b/src/ast/nodes/ObjectExpression.ts @@ -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'; @@ -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; diff --git a/src/ast/nodes/Program.ts b/src/ast/nodes/Program.ts index 28506a38996..7a457f43835 100644 --- a/src/ast/nodes/Program.ts +++ b/src/ast/nodes/Program.ts @@ -38,4 +38,6 @@ export default class Program extends NodeBase { super.render(code, options); } } + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/Property.ts b/src/ast/nodes/Property.ts index c55fac6e834..e8d5b7d6027 100644 --- a/src/ast/nodes/Property.ts +++ b/src/ast/nodes/Property.ts @@ -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[] { diff --git a/src/ast/nodes/PropertyDefinition.ts b/src/ast/nodes/PropertyDefinition.ts index caafa2f6a0d..136d070ddaf 100644 --- a/src/ast/nodes/PropertyDefinition.ts +++ b/src/ast/nodes/PropertyDefinition.ts @@ -73,4 +73,6 @@ export default class PropertyDefinition extends NodeBase { ): boolean { return !this.value || this.value.hasEffectsWhenCalledAtPath(path, callOptions, context); } + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/RestElement.ts b/src/ast/nodes/RestElement.ts index fd20882d8a1..b83fada446a 100644 --- a/src/ast/nodes/RestElement.ts +++ b/src/ast/nodes/RestElement.ts @@ -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( diff --git a/src/ast/nodes/SpreadElement.ts b/src/ast/nodes/SpreadElement.ts index 95145d9c6fd..b6f5849fdbc 100644 --- a/src/ast/nodes/SpreadElement.ts +++ b/src/ast/nodes/SpreadElement.ts @@ -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, diff --git a/src/ast/nodes/UnaryExpression.ts b/src/ast/nodes/UnaryExpression.ts index 6a7ef31bb2d..ae732027e02 100644 --- a/src/ast/nodes/UnaryExpression.ts +++ b/src/ast/nodes/UnaryExpression.ts @@ -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, diff --git a/src/ast/nodes/UpdateExpression.ts b/src/ast/nodes/UpdateExpression.ts index 78b4ccbb7f6..68a4c052718 100644 --- a/src/ast/nodes/UpdateExpression.ts +++ b/src/ast/nodes/UpdateExpression.ts @@ -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(); diff --git a/src/ast/nodes/VariableDeclaration.ts b/src/ast/nodes/VariableDeclaration.ts index d4cffb0958c..cdd0b50578c 100644 --- a/src/ast/nodes/VariableDeclaration.ts +++ b/src/ast/nodes/VariableDeclaration.ts @@ -96,6 +96,8 @@ export default class VariableDeclaration extends NodeBase { } } + protected applyDeoptimizations() {} + private renderDeclarationEnd( code: MagicString, separatorString: string, diff --git a/src/ast/nodes/VariableDeclarator.ts b/src/ast/nodes/VariableDeclarator.ts index e423b4a84f4..f1e7c654928 100644 --- a/src/ast/nodes/VariableDeclarator.ts +++ b/src/ast/nodes/VariableDeclarator.ts @@ -69,4 +69,6 @@ export default class VariableDeclarator extends NodeBase { code.appendLeft(this.end, `${_}=${_}void 0`); } } + + protected applyDeoptimizations() {} } diff --git a/src/ast/nodes/YieldExpression.ts b/src/ast/nodes/YieldExpression.ts index 60f675f3b65..f92c01026e0 100644 --- a/src/ast/nodes/YieldExpression.ts +++ b/src/ast/nodes/YieldExpression.ts @@ -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(); diff --git a/src/ast/nodes/shared/CallExpressionBase.ts b/src/ast/nodes/shared/CallExpressionBase.ts index 4f9c187b23d..72524d773cc 100644 --- a/src/ast/nodes/shared/CallExpressionBase.ts +++ b/src/ast/nodes/shared/CallExpressionBase.ts @@ -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(); diff --git a/src/ast/nodes/shared/ClassNode.ts b/src/ast/nodes/shared/ClassNode.ts index 53bd8429cff..249ea7cf378 100644 --- a/src/ast/nodes/shared/ClassNode.ts +++ b/src/ast/nodes/shared/ClassNode.ts @@ -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'; @@ -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; @@ -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(); } diff --git a/src/ast/nodes/shared/Expression.ts b/src/ast/nodes/shared/Expression.ts index 72c20d5e27e..c231d4c2402 100644 --- a/src/ast/nodes/shared/Expression.ts +++ b/src/ast/nodes/shared/Expression.ts @@ -24,6 +24,8 @@ export interface InclusionOptions { export class ExpressionEntity implements WritableEntity { included = false; + deoptimizeCallParameters(): void {} + deoptimizePath(_path: ObjectPath): void {} deoptimizeThisOnEventAtPath( diff --git a/src/ast/nodes/shared/FunctionBase.ts b/src/ast/nodes/shared/FunctionBase.ts index 44f5b881d84..6bf3f80c5bb 100644 --- a/src/ast/nodes/shared/FunctionBase.ts +++ b/src/ast/nodes/shared/FunctionBase.ts @@ -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) { diff --git a/src/ast/nodes/shared/MethodBase.ts b/src/ast/nodes/shared/MethodBase.ts index 8ad74d66d13..fb606ae81e3 100644 --- a/src/ast/nodes/shared/MethodBase.ts +++ b/src/ast/nodes/shared/MethodBase.ts @@ -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') { diff --git a/src/ast/nodes/shared/Node.ts b/src/ast/nodes/shared/Node.ts index 6e9e4b97c7b..cabbc7f0813 100644 --- a/src/ast/nodes/shared/Node.ts +++ b/src/ast/nodes/shared/Node.ts @@ -96,7 +96,7 @@ export class NodeBase extends ExpressionEntity implements ExpressionNode { // executed code. To do this, they must initialize this as false, implement // applyDeoptimizations and call this from include and hasEffects if they // have custom handlers - protected deoptimized?: boolean; + protected deoptimized = false; constructor( esTreeNode: GenericEsTreeNode, @@ -146,7 +146,7 @@ export class NodeBase extends ExpressionEntity implements ExpressionNode { } hasEffects(context: HasEffectsContext): boolean { - if (this.deoptimized === false) this.applyDeoptimizations(); + if (!this.deoptimized) this.applyDeoptimizations(); for (const key of this.keys) { const value = (this as GenericEsTreeNode)[key]; if (value === null) continue; @@ -164,7 +164,7 @@ export class NodeBase extends ExpressionEntity implements ExpressionNode { includeChildrenRecursively: IncludeChildren, _options?: InclusionOptions ): void { - if (this.deoptimized === false) this.applyDeoptimizations(); + if (!this.deoptimized) this.applyDeoptimizations(); this.included = true; for (const key of this.keys) { const value = (this as GenericEsTreeNode)[key]; diff --git a/src/ast/variables/LocalVariable.ts b/src/ast/variables/LocalVariable.ts index e585ea775a3..bb6e4c3f41b 100644 --- a/src/ast/variables/LocalVariable.ts +++ b/src/ast/variables/LocalVariable.ts @@ -59,6 +59,10 @@ export default class LocalVariable extends Variable { } } + deoptimizeCallParameters() { + this.init?.deoptimizeCallParameters(); + } + deoptimizePath(path: ObjectPath): void { if ( this.isReassigned || diff --git a/test/function/samples/parameter-defaults/arrays/_config.js b/test/function/samples/parameter-defaults/arrays/_config.js new file mode 100644 index 00000000000..22ea9b87836 --- /dev/null +++ b/test/function/samples/parameter-defaults/arrays/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'keeps parameter defaults for array elements' +}; diff --git a/test/function/samples/parameter-defaults/arrays/main.js b/test/function/samples/parameter-defaults/arrays/main.js new file mode 100644 index 00000000000..6bc55609337 --- /dev/null +++ b/test/function/samples/parameter-defaults/arrays/main.js @@ -0,0 +1,6 @@ +const a = (foo = 'fallback a') => assert.strictEqual(foo, 'fallback a'); + +const array = [a, (foo = 'fallback b') => assert.strictEqual(foo, 'fallback b')]; + +array[0](); +array[1](); diff --git a/test/function/samples/parameter-defaults/class-fields/_config.js b/test/function/samples/parameter-defaults/class-fields/_config.js new file mode 100644 index 00000000000..668035e0dce --- /dev/null +++ b/test/function/samples/parameter-defaults/class-fields/_config.js @@ -0,0 +1,4 @@ +module.exports = { + description: 'keeps parameter defaults for class fields', + minNodeVersion: 12 +}; diff --git a/test/function/samples/parameter-defaults/class-fields/main.js b/test/function/samples/parameter-defaults/class-fields/main.js new file mode 100644 index 00000000000..41870a9332d --- /dev/null +++ b/test/function/samples/parameter-defaults/class-fields/main.js @@ -0,0 +1,11 @@ +const a = (foo = 'fallback a') => assert.strictEqual(foo, 'fallback a'); + +const b = (foo = 'fallback b') => assert.strictEqual(foo, 'fallback b'); + +class Test { + static staticField = a; + field = b; +} + +Test.staticField(); +new Test().field(); diff --git a/test/function/samples/parameter-defaults/objects/_config.js b/test/function/samples/parameter-defaults/objects/_config.js new file mode 100644 index 00000000000..d0715d225bc --- /dev/null +++ b/test/function/samples/parameter-defaults/objects/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'keeps parameter defaults for methods in objects' +}; diff --git a/test/function/samples/parameter-defaults/objects/main.js b/test/function/samples/parameter-defaults/objects/main.js new file mode 100644 index 00000000000..89e36b76572 --- /dev/null +++ b/test/function/samples/parameter-defaults/objects/main.js @@ -0,0 +1,11 @@ +const a = (foo = 'fallback a') => assert.strictEqual(foo, 'fallback a'); + +const obj = { + a, + b(foo = 'fallback b') { + assert.strictEqual(foo, 'fallback b'); + } +}; + +obj.a(); +obj.b(); diff --git a/test/function/samples/parameter-defaults/super-classes/main.js b/test/function/samples/parameter-defaults/super-classes/main.js index c83087dbe6b..6e82e24b5ae 100644 --- a/test/function/samples/parameter-defaults/super-classes/main.js +++ b/test/function/samples/parameter-defaults/super-classes/main.js @@ -33,3 +33,11 @@ assert.strictEqual(B.staticMethod(), 'superStaticDefault'); const b = new B(); assert.strictEqual(b.a, 'superConstructorDefault'); assert.strictEqual(b.method(), 'superMethodDefault'); + +function SuperFunction(a = 'functionDefault') { + assert.strictEqual(a, 'functionDefault'); +} + +class C extends SuperFunction {} + +new C();