forked from rollup/rollup
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ParameterVariable.ts
158 lines (148 loc) · 5.46 KB
/
ParameterVariable.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import type { AstContext } from '../../Module';
import { EMPTY_ARRAY } from '../../utils/blank';
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { NodeInteraction } from '../NodeInteractions';
import { INTERACTION_CALLED } from '../NodeInteractions';
import type ExportDefaultDeclaration from '../nodes/ExportDefaultDeclaration';
import type Identifier from '../nodes/Identifier';
import type { ExpressionEntity, LiteralValueOrUnknown } from '../nodes/shared/Expression';
import {
deoptimizeInteraction,
UNKNOWN_EXPRESSION,
UNKNOWN_RETURN_EXPRESSION,
UnknownValue
} from '../nodes/shared/Expression';
import type { ExpressionNode } from '../nodes/shared/Node';
import type { ObjectPath, ObjectPathKey } from '../utils/PathTracker';
import {
PathTracker,
SHARED_RECURSION_TRACKER,
UNKNOWN_PATH,
UnknownKey
} from '../utils/PathTracker';
import LocalVariable from './LocalVariable';
interface DeoptimizationInteraction {
interaction: NodeInteraction;
path: ObjectPath;
}
const MAX_TRACKED_INTERACTIONS = 20;
const NO_INTERACTIONS = EMPTY_ARRAY as unknown as DeoptimizationInteraction[];
const UNKNOWN_DEOPTIMIZED_FIELD = new Set<ObjectPathKey>([UnknownKey]);
const EMPTY_PATH_TRACKER = new PathTracker();
const UNKNOWN_DEOPTIMIZED_ENTITY = new Set<ExpressionEntity>([UNKNOWN_EXPRESSION]);
export default class ParameterVariable extends LocalVariable {
private deoptimizationInteractions: DeoptimizationInteraction[] = [];
private deoptimizations = new PathTracker();
private deoptimizedFields = new Set<ObjectPathKey>();
private entitiesToBeDeoptimized = new Set<ExpressionEntity>();
constructor(
name: string,
declarator: Identifier | ExportDefaultDeclaration | null,
context: AstContext
) {
super(name, declarator, UNKNOWN_EXPRESSION, context, 'parameter');
}
addEntityToBeDeoptimized(entity: ExpressionEntity): void {
if (entity === UNKNOWN_EXPRESSION) {
// As unknown expressions fully deoptimize all interactions, we can clear
// the interaction cache at this point provided we keep this optimization
// in mind when adding new interactions
if (!this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION)) {
this.entitiesToBeDeoptimized.add(UNKNOWN_EXPRESSION);
for (const { interaction } of this.deoptimizationInteractions) {
deoptimizeInteraction(interaction);
}
this.deoptimizationInteractions = NO_INTERACTIONS;
}
} else if (this.deoptimizedFields.has(UnknownKey)) {
// This means that we already deoptimized all interactions and no longer
// track them
entity.deoptimizePath(UNKNOWN_PATH);
} else if (!this.entitiesToBeDeoptimized.has(entity)) {
this.entitiesToBeDeoptimized.add(entity);
for (const field of this.deoptimizedFields) {
entity.deoptimizePath([field]);
}
for (const { interaction, path } of this.deoptimizationInteractions) {
entity.deoptimizeArgumentsOnInteractionAtPath(interaction, path, SHARED_RECURSION_TRACKER);
}
}
}
knownValue: ExpressionNode | null = null;
setKnownValue(value: ExpressionNode): void {
this.knownValue = value;
}
getLiteralValueAtPath(
path: ObjectPath,
recursionTracker: PathTracker,
origin: DeoptimizableEntity
): LiteralValueOrUnknown {
if (this.isReassigned) {
return UnknownValue;
}
if (this.knownValue) {
return this.knownValue.getLiteralValueAtPath(path, recursionTracker, origin);
}
return UnknownValue;
}
deoptimizeArgumentsOnInteractionAtPath(interaction: NodeInteraction, path: ObjectPath): void {
// For performance reasons, we fully deoptimize all deeper interactions
if (
path.length >= 2 ||
this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION) ||
this.deoptimizationInteractions.length >= MAX_TRACKED_INTERACTIONS ||
(path.length === 1 &&
(this.deoptimizedFields.has(UnknownKey) ||
(interaction.type === INTERACTION_CALLED && this.deoptimizedFields.has(path[0]))))
) {
deoptimizeInteraction(interaction);
return;
}
if (!this.deoptimizations.trackEntityAtPathAndGetIfTracked(path, interaction.args)) {
for (const entity of this.entitiesToBeDeoptimized) {
entity.deoptimizeArgumentsOnInteractionAtPath(interaction, path, SHARED_RECURSION_TRACKER);
}
if (!this.entitiesToBeDeoptimized.has(UNKNOWN_EXPRESSION)) {
this.deoptimizationInteractions.push({
interaction,
path
});
}
}
}
deoptimizePath(path: ObjectPath): void {
if (path.length === 0 || this.deoptimizedFields.has(UnknownKey)) {
return;
}
const key = path[0];
if (this.deoptimizedFields.has(key)) {
return;
}
this.deoptimizedFields.add(key);
for (const entity of this.entitiesToBeDeoptimized) {
// We do not need a recursion tracker here as we already track whether
// this field is deoptimized
entity.deoptimizePath([key]);
}
if (key === UnknownKey) {
// save some memory
this.deoptimizationInteractions = NO_INTERACTIONS;
this.deoptimizations = EMPTY_PATH_TRACKER;
this.deoptimizedFields = UNKNOWN_DEOPTIMIZED_FIELD;
this.entitiesToBeDeoptimized = UNKNOWN_DEOPTIMIZED_ENTITY;
}
}
getReturnExpressionWhenCalledAtPath(
path: ObjectPath
): [expression: ExpressionEntity, isPure: boolean] {
// We deoptimize everything that is called as that will trivially deoptimize
// the corresponding return expressions as well and avoid badly performing
// and complicated alternatives
if (path.length === 0) {
this.deoptimizePath(UNKNOWN_PATH);
} else if (!this.deoptimizedFields.has(path[0])) {
this.deoptimizePath([path[0]]);
}
return UNKNOWN_RETURN_EXPRESSION;
}
}