8000 feature(fix): function parameter tracking (#5483) · rollup/rollup@d98e59d · GitHub
[go: up one dir, main page]

Skip to content

Commit d98e59d

Browse files
authored
feature(fix): function parameter tracking (#5483)
* feat: re-init function parameter tracking Revert "Revert function parameter tracking logic for now (#5487)" This reverts commit 74da135. * refactor: see #5483 comments. squashed with some style commits style: move deoptimizeCache logic style: naming style: naming remove useless comment style: small update * make sure every addUsedPlace has a requestTreeshakingPass This is to make sure if onlyFunctionCallUsed becomes false, there is a new tree-shaking pass to deoptimize it * fix: missing reassigned guard
1 parent 778066d commit d98e59d

File tree

90 files changed

+1069
-45
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+1069
-45
lines changed

src/ast/nodes/ArrowFunctionExpression.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import type ChildScope from '../scopes/ChildScope';
55
import ReturnValueScope from '../scopes/ReturnValueScope';
66
import { type ObjectPath } from '../utils/PathTracker';
77
import type BlockStatement from './BlockStatement';
8+
import type CallExpression from './CallExpression';
89
import Identifier from './Identifier';
9-
import type * as NodeType from './NodeType';
10+
import * as NodeType from './NodeType';
1011
import { Flag, isFlagSet, setFlag } from './shared/BitFlags';
1112
import FunctionBase from './shared/FunctionBase';
1213
import type { ExpressionNode, IncludeChildren } from './shared/Node';
@@ -67,6 +68,13 @@ export default class ArrowFunctionExpression extends FunctionBase {
6768
return false;
6869
}
6970

71+
protected onlyFunctionCallUsed(): boolean {
72+
const isIIFE =
73+
this.parent.type === NodeType.CallExpression &&
74+
(this.parent as CallExpression).callee === this;
75+
return isIIFE || super.onlyFunctionCallUsed();
76+
}
77+
7078
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void {
7179
super.include(context, includeChildrenRecursively);
7280
for (const parameter of this.params) {

src/ast/nodes/CallExpression.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,17 @@ export default class CallExpression
6363
}
6464

6565
hasEffects(context: HasEffectsContext): boolean {
66-
try {
67-
for (const argument of this.arguments) {
68-
if (argument.hasEffects(context)) return true;
69-
}
70-
if (this.annotationPure) {
71-
return false;
72-
}
73-
return (
74-
this.callee.hasEffects(context) ||
75-
this.callee.hasEffectsOnInteractionAtPath(EMPTY_PATH, this.interaction, context)
76-
);
77-
} finally {
78-
if (!this.deoptimized) this.applyDeoptimizations();
66+
if (!this.deoptimized) this.applyDeoptimizations();
67+
for (const argument of this.arguments) {
68+
if (argument.hasEffects(context)) return true;
7969
}
70+
if (this.annotationPure) {
71+
return false;
72+
}
73+
return (
74+
this.callee.hasEffects(context) ||
75+
this.callee.hasEffectsOnInteractionAtPath(EMPTY_PATH, this.interaction, context)
76+
);
8077
}
8178

8279
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void {

src/ast/nodes/FunctionDeclaration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ export default class FunctionDeclaration extends FunctionNode {
1414
}
1515
}
1616

17+
protected onlyFunctionCallUsed(): boolean {
18+
// call super.onlyFunctionCallUsed for export default anonymous function
19+
return this.id?.variable.getOnlyFunctionCallUsed() ?? super.onlyFunctionCallUsed();
20+
}
21+
1722
parseNode(esTreeNode: GenericEsTreeNode): this {
1823
if (esTreeNode.id !== null) {
1924
this.id = new Identifier(this, this.scope.parent as ChildScope).parseNode(

src/ast/nodes/FunctionExpression.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type MagicString from 'magic-string';
22
import { BLANK } from '../../utils/blank';
33
import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers';
44
import ChildScope from '../scopes/ChildScope';
5+
import type CallExpression from './CallExpression';
56
import type { IdentifierWithVariable } from './Identifier';
67
import Identifier from './Identifier';
78
import * as NodeType from './NodeType';
@@ -25,6 +26,14 @@ export default class FunctionExpression extends FunctionNode {
2526
return super.parseNode(esTreeNode);
2627
}
2728

29+
protected onlyFunctionCallUsed(): boolean {
30+
const isIIFE =
31+
this.parent.type === NodeType.CallExpression &&
32+
(this.parent as CallExpression).callee === this &&
33+
(this.id === null || this.id.variable.getOnlyFunctionCallUsed());
34+
return isIIFE || super.onlyFunctionCallUsed();
35+
}
36+
2837
render(
2938
code: MagicString,
3039
options: RenderOptions,

src/ast/nodes/Identifier.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,12 @@ export default class Identifier extends NodeBase implements PatternNode {
6060
}
6161
}
6262

63+
private isReferenceVariable = false;
6364
bind(): void {
6465
if (!this.variable && isReference(this, this.parent as NodeWithFieldDefinition)) {
6566
this.variable = this.scope.findVariable(this.name);
6667
this.variable.addReference(this);
68+
this.isReferenceVariable = true;
6769
}
6870
}
6971

@@ -295,6 +297,10 @@ export default class Identifier extends NodeBase implements PatternNode {
295297
this.variable.consolidateInitializers();
296298
this.scope.context.requestTreeshakingPass();
297299
}
300+
if (this.isReferenceVariable) {
301+
this.variable!.addUsedPlace(this);
302+
this.scope.context.requestTreeshakingPass();
303+
}
298304
}
299305

300306
private getVariableRespectingTDZ(): ExpressionEntity | null {

src/ast/nodes/MemberExpression.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,10 @@ export default class MemberExpression
396396
);
397397
this.scope.context.requestTreeshakingPass();
398398
}
399+
if (this.variable) {
400+
this.variable.addUsedPlace(this);
401+
this.scope.context.requestTreeshakingPass();
402+
}
399403
}
400404

401405
private applyAssignmentDeoptimization(): void {

src/ast/nodes/UpdateExpression.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default class UpdateExpression extends NodeBase {
8787
this.argument.deoptimizePath(EMPTY_PATH);
8888
if (this.argument instanceof Identifier) {
8989
const variable = this.scope.findVariable(this.argument.name);
90-
variable.isReassigned = true;
90+
variable.markReassigned();
9191
}
9292
this.scope.context.requestTreeshakingPass();
9393
}

src/ast/nodes/shared/FunctionBase.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ import {
1010
import type ReturnValueScope from '../../scopes/ReturnValueScope';
1111
import type { ObjectPath, PathTracker } from '../../utils/PathTracker';
1212
import { UNKNOWN_PATH, UnknownKey } from '../../utils/PathTracker';
13+
import { UNDEFINED_EXPRESSION } from '../../values';
1314
import type ParameterVariable from '../../variables/ParameterVariable';
15+
import type Variable from '../../variables/Variable';
1416
import BlockStatement from '../BlockStatement';
17+
import type ExportDefaultDeclaration from '../ExportDefaultDeclaration';
1518
import Identifier from '../Identifier';
19+
import * as NodeType from '../NodeType';
1620
import RestElement from '../RestElement';
1721
import type SpreadElement from '../SpreadElement';
22+
import type VariableDeclarator from '../VariableDeclarator';
1823
import { Flag, isFlagSet, setFlag } from './BitFlags';
1924
import type { ExpressionEntity, LiteralValueOrUnknown } from './Expression';
2025
import { UNKNOWN_EXPRESSION, UNKNOWN_RETURN_EXPRESSION } from './Expression';
@@ -27,6 +32,8 @@ import {
2732
import type { ObjectEntity } from './ObjectEntity';
2833
import type { PatternNode } from './Pattern';
2934

35+
type InteractionCalledArguments = NodeInteractionCalled['args'];
36+
3037
export default abstract class FunctionBase extends NodeBase {
3138
declare body: BlockStatement | ExpressionNode;
3239
declare params: PatternNode[];
@@ -57,6 +64,27 @@ export default abstract class FunctionBase extends NodeBase {
5764
this.flags = setFlag(this.flags, Flag.generator, value);
5865
}
5966

67+
private updateParameterVariableValues(_arguments: InteractionCalledArguments): void {
68+
for (let position = 0; position < this.params.length; position++) {
69+
const parameter = this.params[position];
70+
if (!(parameter instanceof Identifier)) {
71+
continue;
72+
}
73+
const parameterVariable = parameter.variable as ParameterVariable;
74+
const argument = _arguments[position + 1] ?? UNDEFINED_EXPRESSION;
75+
parameterVariable.updateKnownValue(argument);
76+
}
77+
}
78+
79+
private deoptimizeParameterVariableValues() {
80+
for (const parameter of this.params) {
81+
if (parameter instanceof Identifier) {
82+
const parameterVariable = parameter.variable as ParameterVariable;
83+
parameterVariable.markReassigned();
84+
}
85+
}
86+
}
87+
6088
protected objectEntity: ObjectEntity | null = null;
6189

6290
deoptimizeArgumentsOnInteractionAtPath(
@@ -84,6 +112,7 @@ export default abstract class FunctionBase extends NodeBase {
84112
this.addArgumentToBeDeoptimized(argument);
85113
}
86114
}
115+
this.updateParameterVariableValues(args);
87116
} else {
88117
this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath(
89118
interaction,
@@ -102,6 +131,7 @@ export default abstract class FunctionBase extends NodeBase {
102131
for (const parameterList of this.scope.parameters) {
103132
for (const parameter of parameterList) {
104133
parameter.deoptimizePath(UNKNOWN_PATH);
134+
parameter.markReassigned();
105135
}
106136
}
107137
}
@@ -180,7 +210,26 @@ export default abstract class FunctionBase extends NodeBase {
180210
return false;
181211
}
182212

213+
/**
214+
* If the function (expression or declaration) is only used as function calls
215+
*/
216+
protected onlyFunctionCallUsed(): boolean {
217+
let variable: Variable | null = null;
218+
if (this.parent.type === NodeType.VariableDeclarator) {
219+
variable = (this.parent as VariableDeclarator).id.variable ?? null;
220+
}
221+
if (this.parent.type === NodeType.ExportDefaultDeclaration) {
222+
variable = (this.parent as ExportDefaultDeclaration).variable;
223+
}
224+
return variable?.getOnlyFunctionCallUsed() ?? false;
225+
}
226+
227+
private parameterVariableValuesDeoptimized = false;
183228
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void {
229+
if (!this.parameterVariableValuesDeoptimized && !this.onlyFunctionCallUsed()) {
230+
this.parameterVariableValuesDeoptimized = true;
231+
this.deoptimizeParameterVariableValues();
232+
}
184233
if (!this.deoptimized) this.applyDeoptimizations();
185234
this.included = true;
186235
const { brokenFlow } = context;

src/ast/variables/ExportDefaultVariable.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ClassDeclaration from '../nodes/ClassDeclaration';
33
import type ExportDefaultDeclaration from '../nodes/ExportDefaultDeclaration';
44
import FunctionDeclaration from '../nodes/FunctionDeclaration';
55
import Identifier, { type IdentifierWithVariable } from '../nodes/Identifier';
6+
import type { NodeBase } from '../nodes/shared/Node';
67
import LocalVariable from './LocalVariable';
78
import UndefinedVariable from './UndefinedVariable';
89
import type Variable from './Variable';
@@ -37,6 +38,15 @@ export default class ExportDefaultVariable extends LocalVariable {
3738
}
3839
}
3940

41+
addUsedPlace(usedPlace: NodeBase): void {
42+
const original = this.getOriginalVariable();
43+
if (original === this) {
44+
super.addUsedPlace(usedPlace);
45+
} else {
46+
original.addUsedPlace(usedPlace);
47+
}
48+
}
49+
4050
forbidName(name: string) {
4151
const original = this.getOriginalVariable();
4252
if (original === this) {

src/ast/variables/GlobalVariable.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import type { ObjectPath, PathTracker } from '../utils/PathTracker';
1313
import Variable from './Variable';
1414

1515
export default class GlobalVariable extends Variable {
16-
// Ensure we use live-bindings for globals as we do not know if they have
17-
// been reassigned
18-
isReassigned = true;
16+
constructor(name: string) {
17+
super(name);
18+
// Ensure we use live-bindings for globals as we do not know if they have
19+
// been reassigned
20+
this.markReassigned();
21+
}
1922

2023
deoptimizeArgumentsOnInteractionAtPath(
2124
interaction: NodeInteraction,

0 commit comments

Comments
 (0)
0