numtypes 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/LICENSE +12 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +652 -0
- package/dist/lib/index.d.ts +22 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/transformer/analyze/analyze-source-file.d.ts +15 -0
- package/dist/transformer/analyze/analyze-source-file.d.ts.map +1 -0
- package/dist/transformer/analyze/analyze-source-file.js +605 -0
- package/dist/transformer/analyze/analyze-source-file.js.map +1 -0
- package/dist/transformer/analyze/get-contextual-domain.d.ts +19 -0
- package/dist/transformer/analyze/get-contextual-domain.d.ts.map +1 -0
- package/dist/transformer/analyze/get-contextual-domain.js +197 -0
- package/dist/transformer/analyze/get-contextual-domain.js.map +1 -0
- package/dist/transformer/analyze/get-expression-domain.d.ts +26 -0
- package/dist/transformer/analyze/get-expression-domain.d.ts.map +1 -0
- package/dist/transformer/analyze/get-expression-domain.js +804 -0
- package/dist/transformer/analyze/get-expression-domain.js.map +1 -0
- package/dist/transformer/analyze/type-domain.d.ts +41 -0
- package/dist/transformer/analyze/type-domain.d.ts.map +1 -0
- package/dist/transformer/analyze/type-domain.js +260 -0
- package/dist/transformer/analyze/type-domain.js.map +1 -0
- package/dist/transformer/ast.d.ts +10 -0
- package/dist/transformer/ast.d.ts.map +1 -0
- package/dist/transformer/ast.js +115 -0
- package/dist/transformer/ast.js.map +1 -0
- package/dist/transformer/diagnostics.d.ts +17 -0
- package/dist/transformer/diagnostics.d.ts.map +1 -0
- package/dist/transformer/diagnostics.js +30 -0
- package/dist/transformer/diagnostics.js.map +1 -0
- package/dist/transformer/domains.d.ts +11 -0
- package/dist/transformer/domains.d.ts.map +1 -0
- package/dist/transformer/domains.js +32 -0
- package/dist/transformer/domains.js.map +1 -0
- package/dist/transformer/index.d.ts +10 -0
- package/dist/transformer/index.d.ts.map +1 -0
- package/dist/transformer/index.js +60 -0
- package/dist/transformer/index.js.map +1 -0
- package/dist/transformer/operators.d.ts +16 -0
- package/dist/transformer/operators.d.ts.map +1 -0
- package/dist/transformer/operators.js +44 -0
- package/dist/transformer/operators.js.map +1 -0
- package/dist/transformer/options.d.ts +19 -0
- package/dist/transformer/options.d.ts.map +1 -0
- package/dist/transformer/options.js +17 -0
- package/dist/transformer/options.js.map +1 -0
- package/dist/transformer/symbols.d.ts +56 -0
- package/dist/transformer/symbols.d.ts.map +1 -0
- package/dist/transformer/symbols.js +270 -0
- package/dist/transformer/symbols.js.map +1 -0
- package/dist/transformer/transform/erase-imports.d.ts +14 -0
- package/dist/transformer/transform/erase-imports.d.ts.map +1 -0
- package/dist/transformer/transform/erase-imports.js +174 -0
- package/dist/transformer/transform/erase-imports.js.map +1 -0
- package/dist/transformer/transform/generated-coercions.d.ts +9 -0
- package/dist/transformer/transform/generated-coercions.d.ts.map +1 -0
- package/dist/transformer/transform/generated-coercions.js +22 -0
- package/dist/transformer/transform/generated-coercions.js.map +1 -0
- package/dist/transformer/transform/optimize-coercions.d.ts +11 -0
- package/dist/transformer/transform/optimize-coercions.d.ts.map +1 -0
- package/dist/transformer/transform/optimize-coercions.js +1702 -0
- package/dist/transformer/transform/optimize-coercions.js.map +1 -0
- package/dist/transformer/transform/transform-declaration-file.d.ts +9 -0
- package/dist/transformer/transform/transform-declaration-file.d.ts.map +1 -0
- package/dist/transformer/transform/transform-declaration-file.js +376 -0
- package/dist/transformer/transform/transform-declaration-file.js.map +1 -0
- package/dist/transformer/transform/transform-expression.d.ts +24 -0
- package/dist/transformer/transform/transform-expression.d.ts.map +1 -0
- package/dist/transformer/transform/transform-expression.js +545 -0
- package/dist/transformer/transform/transform-expression.js.map +1 -0
- package/dist/transformer/transform/transform-source-file.d.ts +10 -0
- package/dist/transformer/transform/transform-source-file.d.ts.map +1 -0
- package/dist/transformer/transform/transform-source-file.js +52 -0
- package/dist/transformer/transform/transform-source-file.js.map +1 -0
- package/dist/transformer/ts-compat.d.ts +4 -0
- package/dist/transformer/ts-compat.d.ts.map +1 -0
- package/dist/transformer/ts-compat.js +24 -0
- package/dist/transformer/ts-compat.js.map +1 -0
- package/docs/implementation-plan.md +335 -0
- package/docs/lib-implementation.md +77 -0
- package/docs/lowering-optimization-spec.md +1020 -0
- package/docs/project-structure.md +52 -0
- package/docs/transform-spec.md +2114 -0
- package/package.json +83 -0
|
@@ -0,0 +1,1702 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
const arithmeticOperators = new Set([
|
|
3
|
+
ts.SyntaxKind.AsteriskAsteriskToken,
|
|
4
|
+
ts.SyntaxKind.AsteriskToken,
|
|
5
|
+
ts.SyntaxKind.MinusToken,
|
|
6
|
+
ts.SyntaxKind.PercentToken,
|
|
7
|
+
ts.SyntaxKind.PlusToken,
|
|
8
|
+
ts.SyntaxKind.SlashToken
|
|
9
|
+
]);
|
|
10
|
+
const bitwiseOperators = new Set([
|
|
11
|
+
ts.SyntaxKind.AmpersandToken,
|
|
12
|
+
ts.SyntaxKind.BarToken,
|
|
13
|
+
ts.SyntaxKind.CaretToken,
|
|
14
|
+
ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken,
|
|
15
|
+
ts.SyntaxKind.GreaterThanGreaterThanToken,
|
|
16
|
+
ts.SyntaxKind.LessThanLessThanToken
|
|
17
|
+
]);
|
|
18
|
+
const compoundAssignmentOperators = new Set([
|
|
19
|
+
ts.SyntaxKind.AmpersandAmpersandEqualsToken,
|
|
20
|
+
ts.SyntaxKind.AmpersandEqualsToken,
|
|
21
|
+
ts.SyntaxKind.AsteriskAsteriskEqualsToken,
|
|
22
|
+
ts.SyntaxKind.AsteriskEqualsToken,
|
|
23
|
+
ts.SyntaxKind.BarBarEqualsToken,
|
|
24
|
+
ts.SyntaxKind.BarEqualsToken,
|
|
25
|
+
ts.SyntaxKind.CaretEqualsToken,
|
|
26
|
+
ts.SyntaxKind.GreaterThanGreaterThanEqualsToken,
|
|
27
|
+
ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
|
|
28
|
+
ts.SyntaxKind.LessThanLessThanEqualsToken,
|
|
29
|
+
ts.SyntaxKind.MinusEqualsToken,
|
|
30
|
+
ts.SyntaxKind.PercentEqualsToken,
|
|
31
|
+
ts.SyntaxKind.PlusEqualsToken,
|
|
32
|
+
ts.SyntaxKind.QuestionQuestionEqualsToken,
|
|
33
|
+
ts.SyntaxKind.SlashEqualsToken
|
|
34
|
+
]);
|
|
35
|
+
export function optimizeCoercions(input) {
|
|
36
|
+
const optimizedSourceFile = new CoercionOptimizer(input).optimizeSourceFile(input.sourceFile);
|
|
37
|
+
return new U32ClosureSinker({
|
|
38
|
+
...input,
|
|
39
|
+
sourceFile: optimizedSourceFile
|
|
40
|
+
}).optimizeSourceFile();
|
|
41
|
+
}
|
|
42
|
+
class CoercionOptimizer {
|
|
43
|
+
checker;
|
|
44
|
+
context;
|
|
45
|
+
factory;
|
|
46
|
+
generatedCoercions;
|
|
47
|
+
optimizeTypedArrayElementAccess;
|
|
48
|
+
typedArrayBoundsFacts = [];
|
|
49
|
+
capturedMutableSymbols = new Set();
|
|
50
|
+
constructor(input) {
|
|
51
|
+
this.checker = input.checker;
|
|
52
|
+
this.context = input.context;
|
|
53
|
+
this.factory = input.context.factory;
|
|
54
|
+
this.generatedCoercions = input.generatedCoercions;
|
|
55
|
+
this.optimizeTypedArrayElementAccess =
|
|
56
|
+
input.optimizeTypedArrayElementAccess;
|
|
57
|
+
}
|
|
58
|
+
optimizeSourceFile(sourceFile) {
|
|
59
|
+
const visitor = (node) => {
|
|
60
|
+
return this.optimizeNodeWithoutFacts(node);
|
|
61
|
+
};
|
|
62
|
+
return ts.visitEachChild(sourceFile, visitor, this.context);
|
|
63
|
+
}
|
|
64
|
+
optimizeNodeWithoutFacts(node) {
|
|
65
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
66
|
+
return this.optimizeFunctionDeclaration(node);
|
|
67
|
+
}
|
|
68
|
+
if (ts.isFunctionExpression(node)) {
|
|
69
|
+
return this.optimizeFunctionExpression(node);
|
|
70
|
+
}
|
|
71
|
+
if (ts.isArrowFunction(node)) {
|
|
72
|
+
return this.optimizeArrowFunction(node);
|
|
73
|
+
}
|
|
74
|
+
if (ts.isMethodDeclaration(node)) {
|
|
75
|
+
return this.optimizeMethodDeclaration(node);
|
|
76
|
+
}
|
|
77
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
78
|
+
return this.optimizeConstructorDeclaration(node);
|
|
79
|
+
}
|
|
80
|
+
if (ts.isGetAccessorDeclaration(node)) {
|
|
81
|
+
return this.optimizeGetAccessorDeclaration(node);
|
|
82
|
+
}
|
|
83
|
+
if (ts.isSetAccessorDeclaration(node)) {
|
|
84
|
+
return this.optimizeSetAccessorDeclaration(node);
|
|
85
|
+
}
|
|
86
|
+
if (ts.isClassStaticBlockDeclaration(node)) {
|
|
87
|
+
return this.factory.updateClassStaticBlockDeclaration(node, this.optimizeBlock(node.body, new Map()));
|
|
88
|
+
}
|
|
89
|
+
return ts.visitEachChild(node, (child) => this.optimizeNodeWithoutFacts(child), this.context);
|
|
90
|
+
}
|
|
91
|
+
optimizeFunctionDeclaration(node) {
|
|
92
|
+
return this.withFunctionScope(node, () => this.factory.updateFunctionDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, this.optimizeOptionalBlock(node.body)));
|
|
93
|
+
}
|
|
94
|
+
optimizeFunctionExpression(node) {
|
|
95
|
+
return this.withFunctionScope(node, () => this.factory.updateFunctionExpression(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, this.optimizeBlock(node.body, new Map())));
|
|
96
|
+
}
|
|
97
|
+
optimizeArrowFunction(node) {
|
|
98
|
+
return this.withFunctionScope(node, () => {
|
|
99
|
+
const facts = new Map();
|
|
100
|
+
const body = ts.isBlock(node.body)
|
|
101
|
+
? this.optimizeBlock(node.body, facts)
|
|
102
|
+
: this.optimizeExpression(node.body, facts).expression;
|
|
103
|
+
return this.factory.updateArrowFunction(node, node.modifiers, node.typeParameters, node.parameters, node.type, node.equalsGreaterThanToken, body);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
optimizeMethodDeclaration(node) {
|
|
107
|
+
return this.withFunctionScope(node, () => this.factory.updateMethodDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, this.optimizeOptionalBlock(node.body)));
|
|
108
|
+
}
|
|
109
|
+
optimizeConstructorDeclaration(node) {
|
|
110
|
+
return this.withFunctionScope(node, () => this.factory.updateConstructorDeclaration(node, node.modifiers, node.parameters, this.optimizeOptionalBlock(node.body)));
|
|
111
|
+
}
|
|
112
|
+
optimizeGetAccessorDeclaration(node) {
|
|
113
|
+
return this.withFunctionScope(node, () => this.factory.updateGetAccessorDeclaration(node, node.modifiers, node.name, node.parameters, node.type, this.optimizeOptionalBlock(node.body)));
|
|
114
|
+
}
|
|
115
|
+
optimizeSetAccessorDeclaration(node) {
|
|
116
|
+
return this.withFunctionScope(node, () => this.factory.updateSetAccessorDeclaration(node, node.modifiers, node.name, node.parameters, this.optimizeOptionalBlock(node.body)));
|
|
117
|
+
}
|
|
118
|
+
withFunctionScope(node, callback) {
|
|
119
|
+
const previousCapturedMutableSymbols = this.capturedMutableSymbols;
|
|
120
|
+
this.capturedMutableSymbols = collectCapturedMutableSymbols(node, this.checker);
|
|
121
|
+
try {
|
|
122
|
+
return callback();
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
this.capturedMutableSymbols = previousCapturedMutableSymbols;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
optimizeOptionalBlock(block) {
|
|
129
|
+
if (block === undefined) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
return this.optimizeBlock(block, new Map());
|
|
133
|
+
}
|
|
134
|
+
optimizeBlock(block, facts) {
|
|
135
|
+
const statements = block.statements.map((statement) => this.optimizeStatement(statement, facts));
|
|
136
|
+
return this.factory.updateBlock(block, statements);
|
|
137
|
+
}
|
|
138
|
+
optimizeStatement(statement, facts) {
|
|
139
|
+
if (ts.isVariableStatement(statement)) {
|
|
140
|
+
return this.optimizeVariableStatement(statement, facts);
|
|
141
|
+
}
|
|
142
|
+
if (ts.isExpressionStatement(statement)) {
|
|
143
|
+
const expression = this.optimizeExpression(statement.expression, facts).expression;
|
|
144
|
+
return this.factory.updateExpressionStatement(statement, expression);
|
|
145
|
+
}
|
|
146
|
+
if (ts.isReturnStatement(statement)) {
|
|
147
|
+
return this.factory.updateReturnStatement(statement, statement.expression === undefined
|
|
148
|
+
? undefined
|
|
149
|
+
: this.optimizeExpression(statement.expression, facts).expression);
|
|
150
|
+
}
|
|
151
|
+
if (ts.isIfStatement(statement)) {
|
|
152
|
+
return this.optimizeIfStatement(statement, facts);
|
|
153
|
+
}
|
|
154
|
+
if (ts.isForStatement(statement)) {
|
|
155
|
+
return this.optimizeForStatement(statement, facts);
|
|
156
|
+
}
|
|
157
|
+
if (ts.isWhileStatement(statement)) {
|
|
158
|
+
return this.optimizeWhileStatement(statement, facts);
|
|
159
|
+
}
|
|
160
|
+
if (ts.isDoStatement(statement)) {
|
|
161
|
+
return this.optimizeDoStatement(statement, facts);
|
|
162
|
+
}
|
|
163
|
+
if (ts.isSwitchStatement(statement)) {
|
|
164
|
+
return this.optimizeSwitchStatement(statement, facts);
|
|
165
|
+
}
|
|
166
|
+
if (ts.isTryStatement(statement)) {
|
|
167
|
+
return this.optimizeTryStatement(statement, facts);
|
|
168
|
+
}
|
|
169
|
+
if (ts.isForInStatement(statement)) {
|
|
170
|
+
return this.optimizeForInStatement(statement, facts);
|
|
171
|
+
}
|
|
172
|
+
if (ts.isForOfStatement(statement)) {
|
|
173
|
+
return this.optimizeForOfStatement(statement, facts);
|
|
174
|
+
}
|
|
175
|
+
if (ts.isLabeledStatement(statement)) {
|
|
176
|
+
return this.factory.updateLabeledStatement(statement, statement.label, this.optimizeStatement(statement.statement, facts));
|
|
177
|
+
}
|
|
178
|
+
if (ts.isBlock(statement)) {
|
|
179
|
+
return this.optimizeBlock(statement, facts);
|
|
180
|
+
}
|
|
181
|
+
if (ts.isFunctionDeclaration(statement)) {
|
|
182
|
+
return this.optimizeFunctionDeclaration(statement);
|
|
183
|
+
}
|
|
184
|
+
facts.clear();
|
|
185
|
+
return ts.visitEachChild(statement, (node) => this.optimizeNodeWithoutFacts(node), this.context);
|
|
186
|
+
}
|
|
187
|
+
optimizeVariableStatement(statement, facts) {
|
|
188
|
+
const declarations = statement.declarationList.declarations.map((declaration) => this.optimizeVariableDeclaration(declaration, facts));
|
|
189
|
+
return this.factory.updateVariableStatement(statement, statement.modifiers, this.factory.updateVariableDeclarationList(statement.declarationList, declarations));
|
|
190
|
+
}
|
|
191
|
+
optimizeVariableDeclaration(declaration, facts) {
|
|
192
|
+
if (declaration.initializer === undefined) {
|
|
193
|
+
this.invalidateBindingName(declaration.name, facts);
|
|
194
|
+
return declaration;
|
|
195
|
+
}
|
|
196
|
+
const initializer = this.optimizeExpression(declaration.initializer, facts);
|
|
197
|
+
this.updateBindingNameFact(declaration.name, initializer.domain, facts);
|
|
198
|
+
return this.factory.updateVariableDeclaration(declaration, declaration.name, declaration.exclamationToken, declaration.type, initializer.expression);
|
|
199
|
+
}
|
|
200
|
+
optimizeIfStatement(statement, facts) {
|
|
201
|
+
const expression = this.optimizeExpression(statement.expression, facts).expression;
|
|
202
|
+
const thenFacts = cloneFacts(facts);
|
|
203
|
+
const elseFacts = cloneFacts(facts);
|
|
204
|
+
const thenStatement = this.optimizeStatement(statement.thenStatement, thenFacts);
|
|
205
|
+
const elseStatement = statement.elseStatement === undefined
|
|
206
|
+
? undefined
|
|
207
|
+
: this.optimizeStatement(statement.elseStatement, elseFacts);
|
|
208
|
+
const joinedFacts = statement.elseStatement === undefined
|
|
209
|
+
? joinFacts(thenFacts, facts)
|
|
210
|
+
: joinFacts(thenFacts, elseFacts);
|
|
211
|
+
facts.clear();
|
|
212
|
+
copyFacts(joinedFacts, facts);
|
|
213
|
+
return this.factory.updateIfStatement(statement, expression, thenStatement, elseStatement);
|
|
214
|
+
}
|
|
215
|
+
optimizeForStatement(statement, facts) {
|
|
216
|
+
const initializer = statement.initializer === undefined
|
|
217
|
+
? undefined
|
|
218
|
+
: this.optimizeForInitializer(statement.initializer, facts);
|
|
219
|
+
const condition = statement.condition === undefined
|
|
220
|
+
? undefined
|
|
221
|
+
: this.optimizeExpression(statement.condition, facts).expression;
|
|
222
|
+
const entryFacts = cloneFacts(facts);
|
|
223
|
+
const bodyFacts = cloneFacts(entryFacts);
|
|
224
|
+
const typedArrayBoundsFacts = this.getForLoopTypedArrayBoundsFacts(statement);
|
|
225
|
+
const optimizedStatement = this.withTypedArrayBoundsFacts(typedArrayBoundsFacts, () => this.optimizeStatement(statement.statement, bodyFacts));
|
|
226
|
+
const incrementor = statement.incrementor === undefined
|
|
227
|
+
? undefined
|
|
228
|
+
: this.optimizeExpression(statement.incrementor, bodyFacts).expression;
|
|
229
|
+
const exitFacts = joinFacts(entryFacts, bodyFacts);
|
|
230
|
+
facts.clear();
|
|
231
|
+
copyFacts(exitFacts, facts);
|
|
232
|
+
return this.factory.updateForStatement(statement, initializer, condition, incrementor, optimizedStatement);
|
|
233
|
+
}
|
|
234
|
+
optimizeForInitializer(initializer, facts) {
|
|
235
|
+
if (ts.isVariableDeclarationList(initializer)) {
|
|
236
|
+
const declarations = initializer.declarations.map((declaration) => this.optimizeVariableDeclaration(declaration, facts));
|
|
237
|
+
return this.factory.updateVariableDeclarationList(initializer, declarations);
|
|
238
|
+
}
|
|
239
|
+
return this.optimizeExpression(initializer, facts).expression;
|
|
240
|
+
}
|
|
241
|
+
optimizeWhileStatement(statement, facts) {
|
|
242
|
+
const expression = this.optimizeExpression(statement.expression, facts).expression;
|
|
243
|
+
const entryFacts = cloneFacts(facts);
|
|
244
|
+
const bodyFacts = cloneFacts(entryFacts);
|
|
245
|
+
const optimizedStatement = this.optimizeStatement(statement.statement, bodyFacts);
|
|
246
|
+
const exitFacts = joinFacts(entryFacts, bodyFacts);
|
|
247
|
+
facts.clear();
|
|
248
|
+
copyFacts(exitFacts, facts);
|
|
249
|
+
return this.factory.updateWhileStatement(statement, expression, optimizedStatement);
|
|
250
|
+
}
|
|
251
|
+
optimizeDoStatement(statement, facts) {
|
|
252
|
+
const bodyFacts = cloneFacts(facts);
|
|
253
|
+
const optimizedStatement = this.optimizeStatement(statement.statement, bodyFacts);
|
|
254
|
+
const expression = this.optimizeExpression(statement.expression, bodyFacts).expression;
|
|
255
|
+
facts.clear();
|
|
256
|
+
copyFacts(bodyFacts, facts);
|
|
257
|
+
return this.factory.updateDoStatement(statement, optimizedStatement, expression);
|
|
258
|
+
}
|
|
259
|
+
optimizeSwitchStatement(statement, facts) {
|
|
260
|
+
const expression = this.optimizeExpression(statement.expression, facts).expression;
|
|
261
|
+
const clauseFacts = [];
|
|
262
|
+
const clauses = statement.caseBlock.clauses.map((clause) => {
|
|
263
|
+
const factsForClause = cloneFacts(facts);
|
|
264
|
+
const optimizedClause = ts.isCaseClause(clause)
|
|
265
|
+
? this.optimizeCaseClause(clause, factsForClause)
|
|
266
|
+
: this.optimizeDefaultClause(clause, factsForClause);
|
|
267
|
+
clauseFacts.push(factsForClause);
|
|
268
|
+
return optimizedClause;
|
|
269
|
+
});
|
|
270
|
+
const joinedFacts = clauseFacts.length === 0 ? undefined : joinAllFacts(clauseFacts);
|
|
271
|
+
facts.clear();
|
|
272
|
+
if (joinedFacts !== undefined) {
|
|
273
|
+
copyFacts(joinedFacts, facts);
|
|
274
|
+
}
|
|
275
|
+
return this.factory.updateSwitchStatement(statement, expression, this.factory.updateCaseBlock(statement.caseBlock, clauses));
|
|
276
|
+
}
|
|
277
|
+
optimizeCaseClause(clause, facts) {
|
|
278
|
+
const expression = this.optimizeExpression(clause.expression, facts).expression;
|
|
279
|
+
const statements = clause.statements.map((statement) => this.optimizeStatement(statement, facts));
|
|
280
|
+
return this.factory.updateCaseClause(clause, expression, statements);
|
|
281
|
+
}
|
|
282
|
+
optimizeDefaultClause(clause, facts) {
|
|
283
|
+
const statements = clause.statements.map((statement) => this.optimizeStatement(statement, facts));
|
|
284
|
+
return this.factory.updateDefaultClause(clause, statements);
|
|
285
|
+
}
|
|
286
|
+
optimizeTryStatement(statement, facts) {
|
|
287
|
+
const tryFacts = cloneFacts(facts);
|
|
288
|
+
const tryBlock = this.optimizeBlock(statement.tryBlock, tryFacts);
|
|
289
|
+
let catchClause;
|
|
290
|
+
let joinedFacts = tryFacts;
|
|
291
|
+
if (statement.catchClause !== undefined) {
|
|
292
|
+
const catchFacts = cloneFacts(facts);
|
|
293
|
+
if (statement.catchClause.variableDeclaration !== undefined) {
|
|
294
|
+
this.invalidateBindingName(statement.catchClause.variableDeclaration.name, catchFacts);
|
|
295
|
+
}
|
|
296
|
+
const catchBlock = this.optimizeBlock(statement.catchClause.block, catchFacts);
|
|
297
|
+
catchClause = this.factory.updateCatchClause(statement.catchClause, statement.catchClause.variableDeclaration, catchBlock);
|
|
298
|
+
joinedFacts = joinFacts(tryFacts, catchFacts);
|
|
299
|
+
}
|
|
300
|
+
if (statement.finallyBlock !== undefined) {
|
|
301
|
+
const finallyFacts = cloneFacts(joinedFacts);
|
|
302
|
+
const finallyBlock = this.optimizeBlock(statement.finallyBlock, finallyFacts);
|
|
303
|
+
facts.clear();
|
|
304
|
+
copyFacts(finallyFacts, facts);
|
|
305
|
+
return this.factory.updateTryStatement(statement, tryBlock, catchClause, finallyBlock);
|
|
306
|
+
}
|
|
307
|
+
facts.clear();
|
|
308
|
+
copyFacts(joinedFacts, facts);
|
|
309
|
+
return this.factory.updateTryStatement(statement, tryBlock, catchClause, statement.finallyBlock);
|
|
310
|
+
}
|
|
311
|
+
optimizeForInStatement(statement, facts) {
|
|
312
|
+
const initializer = this.optimizeForInitializer(statement.initializer, facts);
|
|
313
|
+
const expression = this.optimizeExpression(statement.expression, facts).expression;
|
|
314
|
+
const entryFacts = cloneFacts(facts);
|
|
315
|
+
const bodyFacts = cloneFacts(entryFacts);
|
|
316
|
+
this.invalidateForIterationInitializer(initializer, bodyFacts);
|
|
317
|
+
const optimizedStatement = this.optimizeStatement(statement.statement, bodyFacts);
|
|
318
|
+
const exitFacts = joinFacts(entryFacts, bodyFacts);
|
|
319
|
+
facts.clear();
|
|
320
|
+
copyFacts(exitFacts, facts);
|
|
321
|
+
return this.factory.updateForInStatement(statement, initializer, expression, optimizedStatement);
|
|
322
|
+
}
|
|
323
|
+
optimizeForOfStatement(statement, facts) {
|
|
324
|
+
const initializer = this.optimizeForInitializer(statement.initializer, facts);
|
|
325
|
+
const expression = this.optimizeExpression(statement.expression, facts).expression;
|
|
326
|
+
const entryFacts = cloneFacts(facts);
|
|
327
|
+
const bodyFacts = cloneFacts(entryFacts);
|
|
328
|
+
this.invalidateForIterationInitializer(initializer, bodyFacts);
|
|
329
|
+
const optimizedStatement = this.optimizeStatement(statement.statement, bodyFacts);
|
|
330
|
+
const exitFacts = joinFacts(entryFacts, bodyFacts);
|
|
331
|
+
facts.clear();
|
|
332
|
+
copyFacts(exitFacts, facts);
|
|
333
|
+
return this.factory.updateForOfStatement(statement, statement.awaitModifier, initializer, expression, optimizedStatement);
|
|
334
|
+
}
|
|
335
|
+
invalidateForIterationInitializer(initializer, facts) {
|
|
336
|
+
if (ts.isVariableDeclarationList(initializer)) {
|
|
337
|
+
for (const declaration of initializer.declarations) {
|
|
338
|
+
this.invalidateBindingName(declaration.name, facts);
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
this.invalidateAssignmentTarget(initializer, facts);
|
|
343
|
+
}
|
|
344
|
+
optimizeExpression(expression, facts) {
|
|
345
|
+
if (ts.isParenthesizedExpression(expression)) {
|
|
346
|
+
const optimizedExpression = this.optimizeExpression(expression.expression, facts);
|
|
347
|
+
return {
|
|
348
|
+
domain: optimizedExpression.domain,
|
|
349
|
+
expression: this.factory.updateParenthesizedExpression(expression, optimizedExpression.expression)
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
if (ts.isAsExpression(expression)) {
|
|
353
|
+
const optimizedExpression = this.optimizeExpression(expression.expression, facts);
|
|
354
|
+
return {
|
|
355
|
+
domain: optimizedExpression.domain,
|
|
356
|
+
expression: this.factory.updateAsExpression(expression, optimizedExpression.expression, expression.type)
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
if (ts.isTypeAssertionExpression(expression)) {
|
|
360
|
+
const optimizedExpression = this.optimizeExpression(expression.expression, facts);
|
|
361
|
+
return {
|
|
362
|
+
domain: optimizedExpression.domain,
|
|
363
|
+
expression: this.factory.updateTypeAssertion(expression, expression.type, optimizedExpression.expression)
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
if (ts.isNonNullExpression(expression)) {
|
|
367
|
+
const optimizedExpression = this.optimizeExpression(expression.expression, facts);
|
|
368
|
+
return {
|
|
369
|
+
domain: optimizedExpression.domain,
|
|
370
|
+
expression: this.factory.updateNonNullExpression(expression, optimizedExpression.expression)
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
if (ts.isSatisfiesExpression(expression)) {
|
|
374
|
+
const optimizedExpression = this.optimizeExpression(expression.expression, facts);
|
|
375
|
+
return {
|
|
376
|
+
domain: optimizedExpression.domain,
|
|
377
|
+
expression: this.factory.updateSatisfiesExpression(expression, optimizedExpression.expression, expression.type)
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
if (ts.isIdentifier(expression)) {
|
|
381
|
+
return {
|
|
382
|
+
domain: this.getIdentifierFact(expression, facts),
|
|
383
|
+
expression
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
if (ts.isNumericLiteral(expression)) {
|
|
387
|
+
return {
|
|
388
|
+
domain: "f64",
|
|
389
|
+
expression
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
if (ts.isPrefixUnaryExpression(expression)) {
|
|
393
|
+
return this.optimizePrefixUnaryExpression(expression, facts);
|
|
394
|
+
}
|
|
395
|
+
if (ts.isPostfixUnaryExpression(expression)) {
|
|
396
|
+
return this.optimizePostfixUnaryExpression(expression, facts);
|
|
397
|
+
}
|
|
398
|
+
if (ts.isBinaryExpression(expression)) {
|
|
399
|
+
return this.optimizeBinaryExpression(expression, facts);
|
|
400
|
+
}
|
|
401
|
+
if (ts.isCallExpression(expression)) {
|
|
402
|
+
return this.optimizeCallExpression(expression, facts);
|
|
403
|
+
}
|
|
404
|
+
if (ts.isConditionalExpression(expression)) {
|
|
405
|
+
return this.optimizeConditionalExpression(expression, facts);
|
|
406
|
+
}
|
|
407
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
408
|
+
const receiver = this.optimizeExpression(expression.expression, facts).expression;
|
|
409
|
+
this.invalidateReentrySensitiveFacts(facts);
|
|
410
|
+
return {
|
|
411
|
+
domain: undefined,
|
|
412
|
+
expression: this.factory.updatePropertyAccessExpression(expression, receiver, expression.name)
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (ts.isElementAccessExpression(expression)) {
|
|
416
|
+
const typedArrayDomain = this.getBoundedTypedArrayElementDomain(expression);
|
|
417
|
+
const receiver = this.optimizeExpression(expression.expression, facts).expression;
|
|
418
|
+
const argumentExpression = this.optimizeExpression(expression.argumentExpression, facts).expression;
|
|
419
|
+
this.invalidateReentrySensitiveFacts(facts);
|
|
420
|
+
return {
|
|
421
|
+
domain: typedArrayDomain,
|
|
422
|
+
expression: this.factory.updateElementAccessExpression(expression, receiver, argumentExpression)
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
if (ts.isFunctionExpression(expression)) {
|
|
426
|
+
return {
|
|
427
|
+
domain: undefined,
|
|
428
|
+
expression: this.optimizeFunctionExpression(expression)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
if (ts.isArrowFunction(expression)) {
|
|
432
|
+
return {
|
|
433
|
+
domain: undefined,
|
|
434
|
+
expression: this.optimizeArrowFunction(expression)
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
facts.clear();
|
|
438
|
+
return {
|
|
439
|
+
domain: undefined,
|
|
440
|
+
expression: ts.visitEachChild(expression, (node) => this.optimizeNodeWithoutFacts(node), this.context)
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
optimizePrefixUnaryExpression(expression, facts) {
|
|
444
|
+
if (expression.operator === ts.SyntaxKind.PlusPlusToken ||
|
|
445
|
+
expression.operator === ts.SyntaxKind.MinusMinusToken) {
|
|
446
|
+
this.invalidateAssignmentTarget(expression.operand, facts);
|
|
447
|
+
return {
|
|
448
|
+
domain: undefined,
|
|
449
|
+
expression
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const operand = this.optimizeExpression(expression.operand, facts);
|
|
453
|
+
if (expression.operator === ts.SyntaxKind.PlusToken) {
|
|
454
|
+
if (operand.domain === "f64" &&
|
|
455
|
+
this.generatedCoercions.isGeneratedCoercion(expression, "f64")) {
|
|
456
|
+
return operand;
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
domain: "f64",
|
|
460
|
+
expression: this.generatedCoercions.preserveGeneratedCoercion(expression, this.factory.updatePrefixUnaryExpression(expression, operand.expression))
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (expression.operator === ts.SyntaxKind.MinusToken &&
|
|
464
|
+
operand.domain === "f64") {
|
|
465
|
+
return {
|
|
466
|
+
domain: "f64",
|
|
467
|
+
expression: this.factory.updatePrefixUnaryExpression(expression, operand.expression)
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
domain: undefined,
|
|
472
|
+
expression: this.factory.updatePrefixUnaryExpression(expression, operand.expression)
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
optimizePostfixUnaryExpression(expression, facts) {
|
|
476
|
+
this.invalidateAssignmentTarget(expression.operand, facts);
|
|
477
|
+
return {
|
|
478
|
+
domain: undefined,
|
|
479
|
+
expression
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
optimizeBinaryExpression(expression, facts) {
|
|
483
|
+
if (expression.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
484
|
+
return this.optimizeAssignmentExpression(expression, facts);
|
|
485
|
+
}
|
|
486
|
+
if (isCompoundAssignmentOperator(expression.operatorToken.kind)) {
|
|
487
|
+
const left = ts.isIdentifier(expression.left)
|
|
488
|
+
? this.optimizeExpression(expression.left, facts).expression
|
|
489
|
+
: expression.left;
|
|
490
|
+
if (!ts.isIdentifier(expression.left)) {
|
|
491
|
+
facts.clear();
|
|
492
|
+
}
|
|
493
|
+
const right = this.optimizeExpression(expression.right, facts).expression;
|
|
494
|
+
this.invalidateAssignmentTarget(expression.left, facts);
|
|
495
|
+
facts.clear();
|
|
496
|
+
return {
|
|
497
|
+
domain: undefined,
|
|
498
|
+
expression: this.factory.updateBinaryExpression(expression, left, expression.operatorToken, right)
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
const coercionDomain = getBinaryCoercionDomain(expression);
|
|
502
|
+
const isBitwiseExpression = bitwiseOperators.has(expression.operatorToken.kind);
|
|
503
|
+
const left = this.optimizeExpression(expression.left, facts);
|
|
504
|
+
const right = this.optimizeExpression(expression.right, facts);
|
|
505
|
+
if (coercionDomain !== undefined) {
|
|
506
|
+
if (this.generatedCoercions.isGeneratedCoercion(expression, coercionDomain) &&
|
|
507
|
+
canElideCoercion(left.expression, left.domain, coercionDomain, this.generatedCoercions)) {
|
|
508
|
+
return {
|
|
509
|
+
domain: coercionDomain,
|
|
510
|
+
expression: left.expression
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
domain: coercionDomain,
|
|
515
|
+
expression: this.generatedCoercions.preserveGeneratedCoercion(expression, this.factory.updateBinaryExpression(expression, left.expression, expression.operatorToken, right.expression))
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
domain: this.getBinaryExpressionDomain(expression.operatorToken.kind, left.domain, right.domain),
|
|
520
|
+
expression: this.factory.updateBinaryExpression(expression, isBitwiseExpression
|
|
521
|
+
? stripIntegerCoercion(left.expression, this.generatedCoercions)
|
|
522
|
+
: left.expression, expression.operatorToken, isBitwiseExpression
|
|
523
|
+
? stripIntegerCoercion(right.expression, this.generatedCoercions)
|
|
524
|
+
: right.expression)
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
optimizeAssignmentExpression(expression, facts) {
|
|
528
|
+
if (!ts.isIdentifier(expression.left)) {
|
|
529
|
+
this.evaluateAssignmentTarget(expression.left, facts);
|
|
530
|
+
}
|
|
531
|
+
const right = this.optimizeExpression(expression.right, facts);
|
|
532
|
+
this.updateAssignmentTargetFact(expression.left, right.domain, facts);
|
|
533
|
+
return {
|
|
534
|
+
domain: right.domain,
|
|
535
|
+
expression: this.factory.updateBinaryExpression(expression, expression.left, expression.operatorToken, right.expression)
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
evaluateAssignmentTarget(target, facts) {
|
|
539
|
+
if (ts.isIdentifier(target)) {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
if (ts.isParenthesizedExpression(target)) {
|
|
543
|
+
this.evaluateAssignmentTarget(target.expression, facts);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (ts.isElementAccessExpression(target)) {
|
|
547
|
+
this.optimizeExpression(target.expression, facts);
|
|
548
|
+
this.optimizeExpression(target.argumentExpression, facts);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (ts.isPropertyAccessExpression(target)) {
|
|
552
|
+
this.optimizeExpression(target.expression, facts);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
facts.clear();
|
|
556
|
+
}
|
|
557
|
+
optimizeCallExpression(expression, facts) {
|
|
558
|
+
const mathFroundArgument = getMathFroundArgument(expression, this.checker);
|
|
559
|
+
if (mathFroundArgument !== undefined) {
|
|
560
|
+
const optimizedArgument = this.optimizeExpression(mathFroundArgument, facts);
|
|
561
|
+
if (optimizedArgument.domain === "f32" &&
|
|
562
|
+
this.generatedCoercions.isGeneratedCoercion(expression, "f32")) {
|
|
563
|
+
return optimizedArgument;
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
domain: "f32",
|
|
567
|
+
expression: this.generatedCoercions.preserveGeneratedCoercion(expression, this.factory.updateCallExpression(expression, expression.expression, expression.typeArguments, [optimizedArgument.expression]))
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
const mathImulArguments = getMathImulArguments(expression, this.checker);
|
|
571
|
+
if (mathImulArguments !== undefined) {
|
|
572
|
+
const left = this.optimizeExpression(mathImulArguments[0], facts);
|
|
573
|
+
const right = this.optimizeExpression(mathImulArguments[1], facts);
|
|
574
|
+
return {
|
|
575
|
+
domain: "i32",
|
|
576
|
+
expression: this.factory.updateCallExpression(expression, expression.expression, expression.typeArguments, [
|
|
577
|
+
stripIntegerCoercion(left.expression, this.generatedCoercions),
|
|
578
|
+
stripIntegerCoercion(right.expression, this.generatedCoercions)
|
|
579
|
+
])
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
const callExpression = this.optimizeExpression(expression.expression, facts).expression;
|
|
583
|
+
const argumentsArray = expression.arguments.map((argument) => this.optimizeExpression(argument, facts).expression);
|
|
584
|
+
this.invalidateAfterUnknownCall(expression, facts);
|
|
585
|
+
return {
|
|
586
|
+
domain: undefined,
|
|
587
|
+
expression: this.factory.updateCallExpression(expression, callExpression, expression.typeArguments, argumentsArray)
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
optimizeConditionalExpression(expression, facts) {
|
|
591
|
+
const condition = this.optimizeExpression(expression.condition, facts).expression;
|
|
592
|
+
const thenFacts = cloneFacts(facts);
|
|
593
|
+
const elseFacts = cloneFacts(facts);
|
|
594
|
+
const whenTrue = this.optimizeExpression(expression.whenTrue, thenFacts);
|
|
595
|
+
const whenFalse = this.optimizeExpression(expression.whenFalse, elseFacts);
|
|
596
|
+
const joinedFacts = joinFacts(thenFacts, elseFacts);
|
|
597
|
+
facts.clear();
|
|
598
|
+
copyFacts(joinedFacts, facts);
|
|
599
|
+
return {
|
|
600
|
+
domain: whenTrue.domain !== undefined && whenTrue.domain === whenFalse.domain
|
|
601
|
+
? whenTrue.domain
|
|
602
|
+
: undefined,
|
|
603
|
+
expression: this.factory.updateConditionalExpression(expression, condition, expression.questionToken, whenTrue.expression, expression.colonToken, whenFalse.expression)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
withTypedArrayBoundsFacts(facts, callback) {
|
|
607
|
+
if (facts.length === 0) {
|
|
608
|
+
return callback();
|
|
609
|
+
}
|
|
610
|
+
const previousFacts = this.typedArrayBoundsFacts;
|
|
611
|
+
this.typedArrayBoundsFacts = [...previousFacts, ...facts];
|
|
612
|
+
try {
|
|
613
|
+
return callback();
|
|
614
|
+
}
|
|
615
|
+
finally {
|
|
616
|
+
this.typedArrayBoundsFacts = previousFacts;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
getForLoopTypedArrayBoundsFacts(statement) {
|
|
620
|
+
if (!this.optimizeTypedArrayElementAccess ||
|
|
621
|
+
statement.condition === undefined ||
|
|
622
|
+
statement.incrementor === undefined) {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
const indexSymbol = getForLoopIndexSymbol(statement.initializer, this.checker);
|
|
626
|
+
if (indexSymbol === undefined ||
|
|
627
|
+
!isSupportedForLoopIncrementor(statement.incrementor, indexSymbol, this.checker)) {
|
|
628
|
+
return [];
|
|
629
|
+
}
|
|
630
|
+
const boundsFacts = getTypedArrayBoundsFactsFromCondition(statement.condition, indexSymbol, this.checker);
|
|
631
|
+
if (boundsFacts.length === 0) {
|
|
632
|
+
return [];
|
|
633
|
+
}
|
|
634
|
+
const protectedSymbols = new Set([
|
|
635
|
+
indexSymbol,
|
|
636
|
+
...boundsFacts.map((fact) => fact.arraySymbol)
|
|
637
|
+
]);
|
|
638
|
+
return statementWritesAnySymbol(statement.statement, protectedSymbols, this.checker)
|
|
639
|
+
? []
|
|
640
|
+
: boundsFacts;
|
|
641
|
+
}
|
|
642
|
+
getBoundedTypedArrayElementDomain(expression) {
|
|
643
|
+
if (!this.optimizeTypedArrayElementAccess) {
|
|
644
|
+
return undefined;
|
|
645
|
+
}
|
|
646
|
+
const receiverSymbol = ts.isIdentifier(expression.expression)
|
|
647
|
+
? this.checker.getSymbolAtLocation(expression.expression)
|
|
648
|
+
: undefined;
|
|
649
|
+
if (receiverSymbol === undefined) {
|
|
650
|
+
return undefined;
|
|
651
|
+
}
|
|
652
|
+
for (const fact of this.typedArrayBoundsFacts) {
|
|
653
|
+
if (fact.arraySymbol !== receiverSymbol) {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
const offset = getIndexOffset(expression.argumentExpression, fact.indexSymbol, this.checker);
|
|
657
|
+
if (offset !== undefined && offset <= fact.maxOffset) {
|
|
658
|
+
return fact.domain;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return undefined;
|
|
662
|
+
}
|
|
663
|
+
getBinaryExpressionDomain(operator, leftDomain, rightDomain) {
|
|
664
|
+
if (leftDomain === "f64" &&
|
|
665
|
+
rightDomain === "f64" &&
|
|
666
|
+
arithmeticOperators.has(operator)) {
|
|
667
|
+
return "f64";
|
|
668
|
+
}
|
|
669
|
+
if (bitwiseOperators.has(operator)) {
|
|
670
|
+
return operator === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken
|
|
671
|
+
? "u32"
|
|
672
|
+
: "i32";
|
|
673
|
+
}
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
676
|
+
getIdentifierFact(identifier, facts) {
|
|
677
|
+
const symbol = this.checker.getSymbolAtLocation(identifier);
|
|
678
|
+
return symbol === undefined ? undefined : facts.get(symbol);
|
|
679
|
+
}
|
|
680
|
+
updateBindingNameFact(name, domain, facts) {
|
|
681
|
+
if (ts.isIdentifier(name)) {
|
|
682
|
+
this.updateIdentifierFact(name, domain, facts);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
this.invalidateBindingName(name, facts);
|
|
686
|
+
}
|
|
687
|
+
updateAssignmentTargetFact(target, domain, facts) {
|
|
688
|
+
if (ts.isIdentifier(target)) {
|
|
689
|
+
this.updateIdentifierFact(target, domain, facts);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
this.invalidateAssignmentTarget(target, facts);
|
|
693
|
+
this.invalidateReentrySensitiveFacts(facts);
|
|
694
|
+
}
|
|
695
|
+
updateIdentifierFact(identifier, domain, facts) {
|
|
696
|
+
const symbol = this.checker.getSymbolAtLocation(identifier);
|
|
697
|
+
if (symbol === undefined) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (domain === undefined) {
|
|
701
|
+
facts.delete(symbol);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
facts.set(symbol, domain);
|
|
705
|
+
}
|
|
706
|
+
invalidateBindingName(name, facts) {
|
|
707
|
+
if (ts.isIdentifier(name)) {
|
|
708
|
+
this.updateIdentifierFact(name, undefined, facts);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
for (const element of name.elements) {
|
|
712
|
+
if (ts.isBindingElement(element)) {
|
|
713
|
+
this.invalidateBindingName(element.name, facts);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
invalidateAssignmentTarget(target, facts) {
|
|
718
|
+
if (ts.isIdentifier(target)) {
|
|
719
|
+
this.updateIdentifierFact(target, undefined, facts);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
if (ts.isParenthesizedExpression(target)) {
|
|
723
|
+
this.invalidateAssignmentTarget(target.expression, facts);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
if (ts.isElementAccessExpression(target)) {
|
|
727
|
+
this.optimizeExpression(target.expression, facts);
|
|
728
|
+
this.optimizeExpression(target.argumentExpression, facts);
|
|
729
|
+
this.invalidateReentrySensitiveFacts(facts);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (ts.isPropertyAccessExpression(target)) {
|
|
733
|
+
this.optimizeExpression(target.expression, facts);
|
|
734
|
+
this.invalidateReentrySensitiveFacts(facts);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
invalidateAfterUnknownCall(expression, facts) {
|
|
738
|
+
if (ts.isIdentifier(expression.expression) &&
|
|
739
|
+
expression.expression.text === "eval") {
|
|
740
|
+
facts.clear();
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
this.invalidateReentrySensitiveFacts(facts);
|
|
744
|
+
}
|
|
745
|
+
invalidateReentrySensitiveFacts(facts) {
|
|
746
|
+
for (const symbol of this.capturedMutableSymbols) {
|
|
747
|
+
facts.delete(symbol);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
class U32ClosureSinker {
|
|
752
|
+
checker;
|
|
753
|
+
context;
|
|
754
|
+
factory;
|
|
755
|
+
generatedCoercions;
|
|
756
|
+
sourceFile;
|
|
757
|
+
constructor(input) {
|
|
758
|
+
this.checker = input.checker;
|
|
759
|
+
this.context = input.context;
|
|
760
|
+
this.factory = input.context.factory;
|
|
761
|
+
this.generatedCoercions = input.generatedCoercions;
|
|
762
|
+
this.sourceFile = input.sourceFile;
|
|
763
|
+
}
|
|
764
|
+
optimizeSourceFile() {
|
|
765
|
+
return ts.visitEachChild(this.sourceFile, (node) => this.optimizeNodeWithoutFacts(node), this.context);
|
|
766
|
+
}
|
|
767
|
+
optimizeNodeWithoutFacts(node) {
|
|
768
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
769
|
+
return this.factory.updateFunctionDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, this.optimizeOptionalBlock(node.body));
|
|
770
|
+
}
|
|
771
|
+
if (ts.isFunctionExpression(node)) {
|
|
772
|
+
return this.factory.updateFunctionExpression(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, this.optimizeBlock(node.body, new Map()));
|
|
773
|
+
}
|
|
774
|
+
if (ts.isArrowFunction(node)) {
|
|
775
|
+
const facts = new Map();
|
|
776
|
+
const body = ts.isBlock(node.body)
|
|
777
|
+
? this.optimizeBlock(node.body, facts)
|
|
778
|
+
: this.optimizeExpression(node.body, facts, "value").expression;
|
|
779
|
+
return this.factory.updateArrowFunction(node, node.modifiers, node.typeParameters, node.parameters, node.type, node.equalsGreaterThanToken, body);
|
|
780
|
+
}
|
|
781
|
+
if (ts.isMethodDeclaration(node)) {
|
|
782
|
+
return this.factory.updateMethodDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, this.optimizeOptionalBlock(node.body));
|
|
783
|
+
}
|
|
784
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
785
|
+
return this.factory.updateConstructorDeclaration(node, node.modifiers, node.parameters, this.optimizeOptionalBlock(node.body));
|
|
786
|
+
}
|
|
787
|
+
if (ts.isGetAccessorDeclaration(node)) {
|
|
788
|
+
return this.factory.updateGetAccessorDeclaration(node, node.modifiers, node.name, node.parameters, node.type, this.optimizeOptionalBlock(node.body));
|
|
789
|
+
}
|
|
790
|
+
if (ts.isSetAccessorDeclaration(node)) {
|
|
791
|
+
return this.factory.updateSetAccessorDeclaration(node, node.modifiers, node.name, node.parameters, this.optimizeOptionalBlock(node.body));
|
|
792
|
+
}
|
|
793
|
+
if (ts.isClassStaticBlockDeclaration(node)) {
|
|
794
|
+
return this.factory.updateClassStaticBlockDeclaration(node, this.optimizeBlock(node.body, new Map()));
|
|
795
|
+
}
|
|
796
|
+
return ts.visitEachChild(node, (child) => this.optimizeNodeWithoutFacts(child), this.context);
|
|
797
|
+
}
|
|
798
|
+
optimizeOptionalBlock(block) {
|
|
799
|
+
if (block === undefined) {
|
|
800
|
+
return undefined;
|
|
801
|
+
}
|
|
802
|
+
return this.optimizeBlock(block, new Map());
|
|
803
|
+
}
|
|
804
|
+
optimizeBlock(block, facts) {
|
|
805
|
+
const statements = block.statements.map((statement) => this.optimizeStatement(statement, facts));
|
|
806
|
+
return this.factory.updateBlock(block, statements);
|
|
807
|
+
}
|
|
808
|
+
optimizeStatement(statement, facts) {
|
|
809
|
+
if (ts.isVariableStatement(statement)) {
|
|
810
|
+
const declarations = statement.declarationList.declarations.map((declaration) => this.optimizeVariableDeclaration(declaration, facts));
|
|
811
|
+
return this.factory.updateVariableStatement(statement, statement.modifiers, this.factory.updateVariableDeclarationList(statement.declarationList, declarations));
|
|
812
|
+
}
|
|
813
|
+
if (ts.isExpressionStatement(statement)) {
|
|
814
|
+
const expression = this.optimizeExpression(statement.expression, facts, "value").expression;
|
|
815
|
+
return this.factory.updateExpressionStatement(statement, expression);
|
|
816
|
+
}
|
|
817
|
+
if (ts.isReturnStatement(statement)) {
|
|
818
|
+
return this.factory.updateReturnStatement(statement, statement.expression === undefined
|
|
819
|
+
? undefined
|
|
820
|
+
: this.optimizeExpression(statement.expression, facts, "value")
|
|
821
|
+
.expression);
|
|
822
|
+
}
|
|
823
|
+
if (ts.isIfStatement(statement)) {
|
|
824
|
+
return this.optimizeIfStatement(statement, facts);
|
|
825
|
+
}
|
|
826
|
+
if (ts.isForStatement(statement)) {
|
|
827
|
+
return this.optimizeForStatement(statement, facts);
|
|
828
|
+
}
|
|
829
|
+
if (ts.isWhileStatement(statement)) {
|
|
830
|
+
return this.optimizeWhileStatement(statement, facts);
|
|
831
|
+
}
|
|
832
|
+
if (ts.isDoStatement(statement)) {
|
|
833
|
+
return this.optimizeDoStatement(statement, facts);
|
|
834
|
+
}
|
|
835
|
+
if (ts.isBlock(statement)) {
|
|
836
|
+
return this.optimizeBlock(statement, facts);
|
|
837
|
+
}
|
|
838
|
+
if (ts.isFunctionDeclaration(statement)) {
|
|
839
|
+
return this.optimizeNodeWithoutFacts(statement);
|
|
840
|
+
}
|
|
841
|
+
facts.clear();
|
|
842
|
+
return ts.visitEachChild(statement, (node) => this.optimizeNodeWithoutFacts(node), this.context);
|
|
843
|
+
}
|
|
844
|
+
optimizeVariableDeclaration(declaration, facts) {
|
|
845
|
+
if (declaration.initializer === undefined) {
|
|
846
|
+
this.deleteBindingState(declaration.name, facts);
|
|
847
|
+
return declaration;
|
|
848
|
+
}
|
|
849
|
+
const sinkedInitializer = this.trySinkU32Closure(declaration.initializer, facts);
|
|
850
|
+
const initializerResult = sinkedInitializer === undefined
|
|
851
|
+
? this.optimizeExpression(declaration.initializer, facts, "value")
|
|
852
|
+
: {
|
|
853
|
+
expression: sinkedInitializer,
|
|
854
|
+
state: "raw"
|
|
855
|
+
};
|
|
856
|
+
const storedState = sinkedInitializer === undefined
|
|
857
|
+
? getClosedU32StorageState(initializerResult.expression)
|
|
858
|
+
: "raw";
|
|
859
|
+
this.updateBindingState(declaration.name, storedState, facts);
|
|
860
|
+
return this.factory.updateVariableDeclaration(declaration, declaration.name, declaration.exclamationToken, declaration.type, initializerResult.expression);
|
|
861
|
+
}
|
|
862
|
+
optimizeIfStatement(statement, facts) {
|
|
863
|
+
const expression = this.optimizeExpression(statement.expression, facts, "value").expression;
|
|
864
|
+
const thenFacts = cloneU32Facts(facts);
|
|
865
|
+
const elseFacts = cloneU32Facts(facts);
|
|
866
|
+
const thenStatement = this.optimizeStatement(statement.thenStatement, thenFacts);
|
|
867
|
+
const elseStatement = statement.elseStatement === undefined
|
|
868
|
+
? undefined
|
|
869
|
+
: this.optimizeStatement(statement.elseStatement, elseFacts);
|
|
870
|
+
const joinedFacts = statement.elseStatement === undefined
|
|
871
|
+
? joinU32Facts(facts, thenFacts)
|
|
872
|
+
: joinU32Facts(thenFacts, elseFacts);
|
|
873
|
+
facts.clear();
|
|
874
|
+
copyU32Facts(joinedFacts, facts);
|
|
875
|
+
return this.factory.updateIfStatement(statement, expression, thenStatement, elseStatement);
|
|
876
|
+
}
|
|
877
|
+
optimizeForStatement(statement, facts) {
|
|
878
|
+
const initializer = statement.initializer === undefined
|
|
879
|
+
? undefined
|
|
880
|
+
: this.optimizeForInitializer(statement.initializer, facts);
|
|
881
|
+
const condition = statement.condition === undefined
|
|
882
|
+
? undefined
|
|
883
|
+
: this.optimizeExpression(statement.condition, facts, "value").expression;
|
|
884
|
+
const entryFacts = cloneU32Facts(facts);
|
|
885
|
+
const bodyFacts = cloneU32Facts(entryFacts);
|
|
886
|
+
const optimizedStatement = this.optimizeStatement(statement.statement, bodyFacts);
|
|
887
|
+
const incrementor = statement.incrementor === undefined
|
|
888
|
+
? undefined
|
|
889
|
+
: this.optimizeExpression(statement.incrementor, bodyFacts, "value")
|
|
890
|
+
.expression;
|
|
891
|
+
const exitFacts = joinU32Facts(entryFacts, bodyFacts);
|
|
892
|
+
facts.clear();
|
|
893
|
+
copyU32Facts(exitFacts, facts);
|
|
894
|
+
return this.factory.updateForStatement(statement, initializer, condition, incrementor, optimizedStatement);
|
|
895
|
+
}
|
|
896
|
+
optimizeForInitializer(initializer, facts) {
|
|
897
|
+
if (ts.isVariableDeclarationList(initializer)) {
|
|
898
|
+
const declarations = initializer.declarations.map((declaration) => this.optimizeVariableDeclaration(declaration, facts));
|
|
899
|
+
return this.factory.updateVariableDeclarationList(initializer, declarations);
|
|
900
|
+
}
|
|
901
|
+
return this.optimizeExpression(initializer, facts, "value").expression;
|
|
902
|
+
}
|
|
903
|
+
optimizeWhileStatement(statement, facts) {
|
|
904
|
+
const expression = this.optimizeExpression(statement.expression, facts, "value").expression;
|
|
905
|
+
const entryFacts = cloneU32Facts(facts);
|
|
906
|
+
const bodyFacts = cloneU32Facts(entryFacts);
|
|
907
|
+
const optimizedStatement = this.optimizeStatement(statement.statement, bodyFacts);
|
|
908
|
+
const exitFacts = joinU32Facts(entryFacts, bodyFacts);
|
|
909
|
+
facts.clear();
|
|
910
|
+
copyU32Facts(exitFacts, facts);
|
|
911
|
+
return this.factory.updateWhileStatement(statement, expression, optimizedStatement);
|
|
912
|
+
}
|
|
913
|
+
optimizeDoStatement(statement, facts) {
|
|
914
|
+
const bodyFacts = cloneU32Facts(facts);
|
|
915
|
+
const optimizedStatement = this.optimizeStatement(statement.statement, bodyFacts);
|
|
916
|
+
const expression = this.optimizeExpression(statement.expression, bodyFacts, "value").expression;
|
|
917
|
+
facts.clear();
|
|
918
|
+
copyU32Facts(bodyFacts, facts);
|
|
919
|
+
return this.factory.updateDoStatement(statement, optimizedStatement, expression);
|
|
920
|
+
}
|
|
921
|
+
optimizeExpression(expression, facts, expressionContext) {
|
|
922
|
+
if (ts.isParenthesizedExpression(expression)) {
|
|
923
|
+
const optimizedExpression = this.optimizeExpression(expression.expression, facts, expressionContext);
|
|
924
|
+
return {
|
|
925
|
+
expression: this.factory.updateParenthesizedExpression(expression, optimizedExpression.expression),
|
|
926
|
+
state: optimizedExpression.state
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
if (ts.isIdentifier(expression)) {
|
|
930
|
+
const state = this.getIdentifierState(expression, facts);
|
|
931
|
+
if (state === "raw" && expressionContext === "value") {
|
|
932
|
+
return {
|
|
933
|
+
expression: createU32Coercion(this.factory, expression),
|
|
934
|
+
state: "closed"
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
expression,
|
|
939
|
+
state
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
if (ts.isNumericLiteral(expression)) {
|
|
943
|
+
return {
|
|
944
|
+
expression,
|
|
945
|
+
state: isNumericLiteralClosedInDomain(expression, "u32")
|
|
946
|
+
? "closed"
|
|
947
|
+
: undefined
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
if (ts.isBinaryExpression(expression)) {
|
|
951
|
+
return this.optimizeBinaryExpression(expression, facts, expressionContext);
|
|
952
|
+
}
|
|
953
|
+
if (ts.isCallExpression(expression)) {
|
|
954
|
+
const mathImulArguments = getMathImulArguments(expression, this.checker);
|
|
955
|
+
if (mathImulArguments !== undefined) {
|
|
956
|
+
const left = this.optimizeExpression(mathImulArguments[0], facts, "low32").expression;
|
|
957
|
+
const right = this.optimizeExpression(mathImulArguments[1], facts, "low32").expression;
|
|
958
|
+
return {
|
|
959
|
+
expression: this.factory.updateCallExpression(expression, expression.expression, expression.typeArguments, [left, right]),
|
|
960
|
+
state: "raw"
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
const callExpression = this.optimizeExpression(expression.expression, facts, "value").expression;
|
|
964
|
+
const argumentsArray = expression.arguments.map((argument) => this.optimizeExpression(argument, facts, "value").expression);
|
|
965
|
+
facts.clear();
|
|
966
|
+
return {
|
|
967
|
+
expression: this.factory.updateCallExpression(expression, callExpression, expression.typeArguments, argumentsArray),
|
|
968
|
+
state: undefined
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
if (ts.isConditionalExpression(expression)) {
|
|
972
|
+
const condition = this.optimizeExpression(expression.condition, facts, "value").expression;
|
|
973
|
+
const thenFacts = cloneU32Facts(facts);
|
|
974
|
+
const elseFacts = cloneU32Facts(facts);
|
|
975
|
+
const whenTrue = this.optimizeExpression(expression.whenTrue, thenFacts, expressionContext);
|
|
976
|
+
const whenFalse = this.optimizeExpression(expression.whenFalse, elseFacts, expressionContext);
|
|
977
|
+
const joinedFacts = joinU32Facts(thenFacts, elseFacts);
|
|
978
|
+
facts.clear();
|
|
979
|
+
copyU32Facts(joinedFacts, facts);
|
|
980
|
+
return {
|
|
981
|
+
expression: this.factory.updateConditionalExpression(expression, condition, expression.questionToken, whenTrue.expression, expression.colonToken, whenFalse.expression),
|
|
982
|
+
state: whenTrue.state === whenFalse.state ? whenTrue.state : undefined
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
986
|
+
const receiver = this.optimizeExpression(expression.expression, facts, "value").expression;
|
|
987
|
+
return {
|
|
988
|
+
expression: this.factory.updatePropertyAccessExpression(expression, receiver, expression.name),
|
|
989
|
+
state: undefined
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
if (ts.isElementAccessExpression(expression)) {
|
|
993
|
+
const receiver = this.optimizeExpression(expression.expression, facts, "value").expression;
|
|
994
|
+
const argumentExpression = this.optimizeExpression(expression.argumentExpression, facts, "value").expression;
|
|
995
|
+
return {
|
|
996
|
+
expression: this.factory.updateElementAccessExpression(expression, receiver, argumentExpression),
|
|
997
|
+
state: undefined
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
if (ts.isFunctionExpression(expression)) {
|
|
1001
|
+
return {
|
|
1002
|
+
expression: this.optimizeNodeWithoutFacts(expression),
|
|
1003
|
+
state: undefined
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
if (ts.isArrowFunction(expression)) {
|
|
1007
|
+
return {
|
|
1008
|
+
expression: this.optimizeNodeWithoutFacts(expression),
|
|
1009
|
+
state: undefined
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
return {
|
|
1013
|
+
expression: ts.visitEachChild(expression, (node) => this.optimizeNodeWithoutFacts(node), this.context),
|
|
1014
|
+
state: undefined
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
optimizeBinaryExpression(expression, facts, expressionContext) {
|
|
1018
|
+
if (expression.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
1019
|
+
return this.optimizeAssignmentExpression(expression, facts);
|
|
1020
|
+
}
|
|
1021
|
+
const coercionDomain = getBinaryCoercionDomain(expression);
|
|
1022
|
+
if (coercionDomain === "u32") {
|
|
1023
|
+
const left = this.optimizeExpression(expression.left, facts, "low32");
|
|
1024
|
+
if (expressionContext === "low32" &&
|
|
1025
|
+
this.generatedCoercions.isGeneratedCoercion(expression, "u32")) {
|
|
1026
|
+
return {
|
|
1027
|
+
expression: left.expression,
|
|
1028
|
+
state: left.state
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
return {
|
|
1032
|
+
expression: this.generatedCoercions.preserveGeneratedCoercion(expression, this.factory.updateBinaryExpression(expression, left.expression, expression.operatorToken, expression.right)),
|
|
1033
|
+
state: "closed"
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
if (bitwiseOperators.has(expression.operatorToken.kind)) {
|
|
1037
|
+
const left = this.optimizeExpression(expression.left, facts, "low32").expression;
|
|
1038
|
+
const right = this.optimizeExpression(expression.right, facts, "low32").expression;
|
|
1039
|
+
return {
|
|
1040
|
+
expression: this.factory.updateBinaryExpression(expression, left, expression.operatorToken, right),
|
|
1041
|
+
state: expression.operatorToken.kind ===
|
|
1042
|
+
ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken
|
|
1043
|
+
? "closed"
|
|
1044
|
+
: "raw"
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
const left = this.optimizeExpression(expression.left, facts, "value").expression;
|
|
1048
|
+
const right = this.optimizeExpression(expression.right, facts, "value").expression;
|
|
1049
|
+
return {
|
|
1050
|
+
expression: this.factory.updateBinaryExpression(expression, left, expression.operatorToken, right),
|
|
1051
|
+
state: undefined
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
optimizeAssignmentExpression(expression, facts) {
|
|
1055
|
+
const targetSymbol = ts.isIdentifier(expression.left)
|
|
1056
|
+
? this.checker.getSymbolAtLocation(expression.left)
|
|
1057
|
+
: undefined;
|
|
1058
|
+
const sinkedRight = targetSymbol !== undefined && facts.has(targetSymbol)
|
|
1059
|
+
? this.trySinkU32Closure(expression.right, facts)
|
|
1060
|
+
: undefined;
|
|
1061
|
+
if (!ts.isIdentifier(expression.left)) {
|
|
1062
|
+
this.optimizeAssignmentTarget(expression.left, facts);
|
|
1063
|
+
}
|
|
1064
|
+
const right = sinkedRight === undefined
|
|
1065
|
+
? this.optimizeExpression(expression.right, facts, "value")
|
|
1066
|
+
: {
|
|
1067
|
+
expression: sinkedRight,
|
|
1068
|
+
state: "raw"
|
|
1069
|
+
};
|
|
1070
|
+
if (targetSymbol !== undefined) {
|
|
1071
|
+
const storedState = sinkedRight === undefined
|
|
1072
|
+
? getClosedU32StorageState(right.expression)
|
|
1073
|
+
: "raw";
|
|
1074
|
+
this.updateIdentifierState(targetSymbol, storedState, facts);
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
facts.clear();
|
|
1078
|
+
}
|
|
1079
|
+
return {
|
|
1080
|
+
expression: this.factory.updateBinaryExpression(expression, expression.left, expression.operatorToken, right.expression),
|
|
1081
|
+
state: right.state
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
optimizeAssignmentTarget(target, facts) {
|
|
1085
|
+
if (ts.isParenthesizedExpression(target)) {
|
|
1086
|
+
this.optimizeAssignmentTarget(target.expression, facts);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
if (ts.isElementAccessExpression(target)) {
|
|
1090
|
+
this.optimizeExpression(target.expression, facts, "value");
|
|
1091
|
+
this.optimizeExpression(target.argumentExpression, facts, "value");
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
if (ts.isPropertyAccessExpression(target)) {
|
|
1095
|
+
this.optimizeExpression(target.expression, facts, "value");
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
facts.clear();
|
|
1099
|
+
}
|
|
1100
|
+
trySinkU32Closure(expression, facts) {
|
|
1101
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1102
|
+
if (!ts.isBinaryExpression(unwrappedExpression)) {
|
|
1103
|
+
return undefined;
|
|
1104
|
+
}
|
|
1105
|
+
if (getBinaryCoercionDomain(unwrappedExpression) !== "u32") {
|
|
1106
|
+
return undefined;
|
|
1107
|
+
}
|
|
1108
|
+
if (!this.generatedCoercions.isGeneratedCoercion(unwrappedExpression, "u32")) {
|
|
1109
|
+
return undefined;
|
|
1110
|
+
}
|
|
1111
|
+
if (!this.isSinkableLow32Expression(unwrappedExpression.left, facts)) {
|
|
1112
|
+
return undefined;
|
|
1113
|
+
}
|
|
1114
|
+
return this.optimizeExpression(unwrappedExpression.left, facts, "low32").expression;
|
|
1115
|
+
}
|
|
1116
|
+
isSinkableLow32Expression(expression, facts) {
|
|
1117
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1118
|
+
if (ts.isIdentifier(unwrappedExpression)) {
|
|
1119
|
+
const symbol = this.checker.getSymbolAtLocation(unwrappedExpression);
|
|
1120
|
+
return symbol !== undefined && facts.has(symbol);
|
|
1121
|
+
}
|
|
1122
|
+
if (ts.isNumericLiteral(unwrappedExpression)) {
|
|
1123
|
+
return isNumericLiteralClosedInDomain(unwrappedExpression, "u32");
|
|
1124
|
+
}
|
|
1125
|
+
if (ts.isBinaryExpression(unwrappedExpression)) {
|
|
1126
|
+
return bitwiseOperators.has(unwrappedExpression.operatorToken.kind);
|
|
1127
|
+
}
|
|
1128
|
+
return (ts.isCallExpression(unwrappedExpression) &&
|
|
1129
|
+
getMathImulArguments(unwrappedExpression, this.checker) !== undefined);
|
|
1130
|
+
}
|
|
1131
|
+
getIdentifierState(identifier, facts) {
|
|
1132
|
+
const symbol = this.checker.getSymbolAtLocation(identifier);
|
|
1133
|
+
return symbol === undefined ? undefined : facts.get(symbol);
|
|
1134
|
+
}
|
|
1135
|
+
updateBindingState(name, state, facts) {
|
|
1136
|
+
if (ts.isIdentifier(name)) {
|
|
1137
|
+
const symbol = this.checker.getSymbolAtLocation(name);
|
|
1138
|
+
if (symbol !== undefined) {
|
|
1139
|
+
this.updateIdentifierState(symbol, state, facts);
|
|
1140
|
+
}
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
this.deleteBindingState(name, facts);
|
|
1144
|
+
}
|
|
1145
|
+
updateIdentifierState(symbol, state, facts) {
|
|
1146
|
+
if (state === undefined) {
|
|
1147
|
+
facts.delete(symbol);
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
facts.set(symbol, state);
|
|
1151
|
+
}
|
|
1152
|
+
deleteBindingState(name, facts) {
|
|
1153
|
+
if (ts.isIdentifier(name)) {
|
|
1154
|
+
const symbol = this.checker.getSymbolAtLocation(name);
|
|
1155
|
+
if (symbol !== undefined) {
|
|
1156
|
+
facts.delete(symbol);
|
|
1157
|
+
}
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
for (const element of name.elements) {
|
|
1161
|
+
if (ts.isBindingElement(element)) {
|
|
1162
|
+
this.deleteBindingState(element.name, facts);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
function getBinaryCoercionDomain(expression) {
|
|
1168
|
+
if (!isZeroLiteral(expression.right)) {
|
|
1169
|
+
return undefined;
|
|
1170
|
+
}
|
|
1171
|
+
if (expression.operatorToken.kind === ts.SyntaxKind.BarToken) {
|
|
1172
|
+
return "i32";
|
|
1173
|
+
}
|
|
1174
|
+
if (expression.operatorToken.kind ===
|
|
1175
|
+
ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
|
|
1176
|
+
return "u32";
|
|
1177
|
+
}
|
|
1178
|
+
return undefined;
|
|
1179
|
+
}
|
|
1180
|
+
function createU32Coercion(factory, expression) {
|
|
1181
|
+
return factory.createBinaryExpression(expression, factory.createToken(ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken), factory.createNumericLiteral(0));
|
|
1182
|
+
}
|
|
1183
|
+
function getClosedU32StorageState(expression) {
|
|
1184
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1185
|
+
if (ts.isNumericLiteral(unwrappedExpression)) {
|
|
1186
|
+
return isNumericLiteralClosedInDomain(unwrappedExpression, "u32")
|
|
1187
|
+
? "closed"
|
|
1188
|
+
: undefined;
|
|
1189
|
+
}
|
|
1190
|
+
if (!ts.isBinaryExpression(unwrappedExpression)) {
|
|
1191
|
+
return undefined;
|
|
1192
|
+
}
|
|
1193
|
+
return getBinaryCoercionDomain(unwrappedExpression) === "u32"
|
|
1194
|
+
? "closed"
|
|
1195
|
+
: undefined;
|
|
1196
|
+
}
|
|
1197
|
+
function unwrapParentheses(expression) {
|
|
1198
|
+
let current = expression;
|
|
1199
|
+
while (ts.isParenthesizedExpression(current)) {
|
|
1200
|
+
current = current.expression;
|
|
1201
|
+
}
|
|
1202
|
+
return current;
|
|
1203
|
+
}
|
|
1204
|
+
function cloneU32Facts(facts) {
|
|
1205
|
+
return new Map(facts);
|
|
1206
|
+
}
|
|
1207
|
+
function copyU32Facts(source, target) {
|
|
1208
|
+
for (const [symbol, state] of source) {
|
|
1209
|
+
target.set(symbol, state);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
function joinU32Facts(left, right) {
|
|
1213
|
+
const joinedFacts = new Map();
|
|
1214
|
+
for (const [symbol, leftState] of left) {
|
|
1215
|
+
const rightState = right.get(symbol);
|
|
1216
|
+
if (rightState === undefined) {
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
joinedFacts.set(symbol, leftState === "raw" || rightState === "raw" ? "raw" : "closed");
|
|
1220
|
+
}
|
|
1221
|
+
return joinedFacts;
|
|
1222
|
+
}
|
|
1223
|
+
function isZeroLiteral(expression) {
|
|
1224
|
+
return ts.isNumericLiteral(expression) && expression.text === "0";
|
|
1225
|
+
}
|
|
1226
|
+
function canElideCoercion(expression, expressionDomain, coercionDomain, generatedCoercions) {
|
|
1227
|
+
return (expressionDomain === coercionDomain ||
|
|
1228
|
+
isNumericLiteralClosedInDomain(expression, coercionDomain) ||
|
|
1229
|
+
(coercionDomain === "u32" &&
|
|
1230
|
+
isU32ClosedProducerExpression(expression, generatedCoercions)));
|
|
1231
|
+
}
|
|
1232
|
+
function isU32ClosedProducerExpression(expression, generatedCoercions) {
|
|
1233
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1234
|
+
if (!ts.isBinaryExpression(unwrappedExpression)) {
|
|
1235
|
+
return false;
|
|
1236
|
+
}
|
|
1237
|
+
if (getBinaryCoercionDomain(unwrappedExpression) === "u32" &&
|
|
1238
|
+
generatedCoercions.isGeneratedCoercion(unwrappedExpression, "u32")) {
|
|
1239
|
+
return true;
|
|
1240
|
+
}
|
|
1241
|
+
if (unwrappedExpression.operatorToken.kind === ts.SyntaxKind.AmpersandToken &&
|
|
1242
|
+
(isNonNegativeInt31Mask(unwrappedExpression.left) ||
|
|
1243
|
+
isNonNegativeInt31Mask(unwrappedExpression.right))) {
|
|
1244
|
+
return true;
|
|
1245
|
+
}
|
|
1246
|
+
if (unwrappedExpression.operatorToken.kind === ts.SyntaxKind.PercentToken &&
|
|
1247
|
+
isPositiveIntegerLiteral(unwrappedExpression.right) &&
|
|
1248
|
+
isU32ClosedProducerExpression(unwrappedExpression.left, generatedCoercions)) {
|
|
1249
|
+
return true;
|
|
1250
|
+
}
|
|
1251
|
+
return false;
|
|
1252
|
+
}
|
|
1253
|
+
function isNonNegativeInt31Mask(expression) {
|
|
1254
|
+
const value = getNumericLiteralValue(expression);
|
|
1255
|
+
return (value !== undefined &&
|
|
1256
|
+
Number.isInteger(value) &&
|
|
1257
|
+
value >= 0 &&
|
|
1258
|
+
value <= 0x7fffffff);
|
|
1259
|
+
}
|
|
1260
|
+
function isPositiveIntegerLiteral(expression) {
|
|
1261
|
+
const value = getNumericLiteralValue(expression);
|
|
1262
|
+
return value !== undefined && Number.isInteger(value) && value > 0;
|
|
1263
|
+
}
|
|
1264
|
+
function getNumericLiteralValue(expression) {
|
|
1265
|
+
const literal = getUnwrappedNumericLiteral(expression);
|
|
1266
|
+
return literal === undefined ? undefined : Number(literal.text);
|
|
1267
|
+
}
|
|
1268
|
+
function stripIntegerCoercion(expression, generatedCoercions) {
|
|
1269
|
+
if (ts.isParenthesizedExpression(expression)) {
|
|
1270
|
+
const strippedExpression = stripIntegerCoercion(expression.expression, generatedCoercions);
|
|
1271
|
+
if (strippedExpression === expression.expression) {
|
|
1272
|
+
return expression;
|
|
1273
|
+
}
|
|
1274
|
+
return strippedExpression;
|
|
1275
|
+
}
|
|
1276
|
+
if (!ts.isBinaryExpression(expression)) {
|
|
1277
|
+
return expression;
|
|
1278
|
+
}
|
|
1279
|
+
const coercionDomain = getBinaryCoercionDomain(expression);
|
|
1280
|
+
if (coercionDomain !== "i32" && coercionDomain !== "u32") {
|
|
1281
|
+
return expression;
|
|
1282
|
+
}
|
|
1283
|
+
if (!generatedCoercions.isGeneratedCoercion(expression, coercionDomain)) {
|
|
1284
|
+
return expression;
|
|
1285
|
+
}
|
|
1286
|
+
return expression.left;
|
|
1287
|
+
}
|
|
1288
|
+
function isNumericLiteralClosedInDomain(expression, domain) {
|
|
1289
|
+
const literal = getUnwrappedNumericLiteral(expression);
|
|
1290
|
+
if (literal === undefined) {
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
const value = Number(literal.text);
|
|
1294
|
+
if (!Number.isInteger(value)) {
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
switch (domain) {
|
|
1298
|
+
case "f32":
|
|
1299
|
+
return Math.fround(value) === value;
|
|
1300
|
+
case "f64":
|
|
1301
|
+
return true;
|
|
1302
|
+
case "i32":
|
|
1303
|
+
return value >= -2147483648 && value <= 2147483647;
|
|
1304
|
+
case "u32":
|
|
1305
|
+
return value >= 0 && value <= 4294967295;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
function getUnwrappedNumericLiteral(expression) {
|
|
1309
|
+
let current = expression;
|
|
1310
|
+
while (ts.isParenthesizedExpression(current)) {
|
|
1311
|
+
current = current.expression;
|
|
1312
|
+
}
|
|
1313
|
+
return ts.isNumericLiteral(current) ? current : undefined;
|
|
1314
|
+
}
|
|
1315
|
+
function getMathFroundArgument(expression, checker) {
|
|
1316
|
+
if (expression.arguments.length !== 1) {
|
|
1317
|
+
return undefined;
|
|
1318
|
+
}
|
|
1319
|
+
const argument = expression.arguments[0];
|
|
1320
|
+
if (argument === undefined) {
|
|
1321
|
+
return undefined;
|
|
1322
|
+
}
|
|
1323
|
+
if (!ts.isPropertyAccessExpression(expression.expression)) {
|
|
1324
|
+
return undefined;
|
|
1325
|
+
}
|
|
1326
|
+
const isMathFround = ts.isIdentifier(expression.expression.expression) &&
|
|
1327
|
+
isMathIntrinsicIdentifier(expression.expression.expression, checker) &&
|
|
1328
|
+
expression.expression.expression.text === "Math" &&
|
|
1329
|
+
expression.expression.name.text === "fround";
|
|
1330
|
+
return isMathFround ? argument : undefined;
|
|
1331
|
+
}
|
|
1332
|
+
function getMathImulArguments(expression, checker) {
|
|
1333
|
+
if (expression.arguments.length !== 2) {
|
|
1334
|
+
return undefined;
|
|
1335
|
+
}
|
|
1336
|
+
const left = expression.arguments[0];
|
|
1337
|
+
const right = expression.arguments[1];
|
|
1338
|
+
if (left === undefined || right === undefined) {
|
|
1339
|
+
return undefined;
|
|
1340
|
+
}
|
|
1341
|
+
if (!ts.isPropertyAccessExpression(expression.expression)) {
|
|
1342
|
+
return undefined;
|
|
1343
|
+
}
|
|
1344
|
+
const isMathImul = ts.isIdentifier(expression.expression.expression) &&
|
|
1345
|
+
isMathIntrinsicIdentifier(expression.expression.expression, checker) &&
|
|
1346
|
+
expression.expression.expression.text === "Math" &&
|
|
1347
|
+
expression.expression.name.text === "imul";
|
|
1348
|
+
return isMathImul ? [left, right] : undefined;
|
|
1349
|
+
}
|
|
1350
|
+
function isMathIntrinsicIdentifier(identifier, checker) {
|
|
1351
|
+
const symbol = checker.getSymbolAtLocation(identifier);
|
|
1352
|
+
if (symbol === undefined) {
|
|
1353
|
+
return identifier.pos < 0;
|
|
1354
|
+
}
|
|
1355
|
+
const declarations = symbol.declarations;
|
|
1356
|
+
if (declarations === undefined || declarations.length === 0) {
|
|
1357
|
+
return false;
|
|
1358
|
+
}
|
|
1359
|
+
return declarations.every((declaration) => declaration.getSourceFile().isDeclarationFile);
|
|
1360
|
+
}
|
|
1361
|
+
function isCompoundAssignmentOperator(operator) {
|
|
1362
|
+
return compoundAssignmentOperators.has(operator);
|
|
1363
|
+
}
|
|
1364
|
+
function getForLoopIndexSymbol(initializer, checker) {
|
|
1365
|
+
if (initializer === undefined) {
|
|
1366
|
+
return undefined;
|
|
1367
|
+
}
|
|
1368
|
+
if (ts.isVariableDeclarationList(initializer)) {
|
|
1369
|
+
if (initializer.declarations.length !== 1) {
|
|
1370
|
+
return undefined;
|
|
1371
|
+
}
|
|
1372
|
+
const declaration = initializer.declarations[0];
|
|
1373
|
+
if (declaration === undefined ||
|
|
1374
|
+
!ts.isIdentifier(declaration.name) ||
|
|
1375
|
+
declaration.initializer === undefined ||
|
|
1376
|
+
!isZeroNumberExpression(declaration.initializer)) {
|
|
1377
|
+
return undefined;
|
|
1378
|
+
}
|
|
1379
|
+
return checker.getSymbolAtLocation(declaration.name);
|
|
1380
|
+
}
|
|
1381
|
+
if (ts.isBinaryExpression(initializer) &&
|
|
1382
|
+
initializer.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
1383
|
+
ts.isIdentifier(initializer.left) &&
|
|
1384
|
+
isZeroNumberExpression(initializer.right)) {
|
|
1385
|
+
return checker.getSymbolAtLocation(initializer.left);
|
|
1386
|
+
}
|
|
1387
|
+
return undefined;
|
|
1388
|
+
}
|
|
1389
|
+
function isSupportedForLoopIncrementor(incrementor, indexSymbol, checker) {
|
|
1390
|
+
const expression = unwrapParentheses(incrementor);
|
|
1391
|
+
if ((ts.isPrefixUnaryExpression(expression) ||
|
|
1392
|
+
ts.isPostfixUnaryExpression(expression)) &&
|
|
1393
|
+
expression.operator === ts.SyntaxKind.PlusPlusToken) {
|
|
1394
|
+
return isIdentifierForSymbol(expression.operand, indexSymbol, checker);
|
|
1395
|
+
}
|
|
1396
|
+
if (ts.isBinaryExpression(expression) &&
|
|
1397
|
+
expression.operatorToken.kind === ts.SyntaxKind.PlusEqualsToken &&
|
|
1398
|
+
isIdentifierForSymbol(expression.left, indexSymbol, checker)) {
|
|
1399
|
+
return isPositiveIntegerExpression(expression.right);
|
|
1400
|
+
}
|
|
1401
|
+
if (ts.isBinaryExpression(expression) &&
|
|
1402
|
+
expression.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
1403
|
+
isIdentifierForSymbol(expression.left, indexSymbol, checker)) {
|
|
1404
|
+
return isIndexPlusPositiveInteger(expression.right, indexSymbol, checker);
|
|
1405
|
+
}
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
function getTypedArrayBoundsFactsFromCondition(condition, indexSymbol, checker) {
|
|
1409
|
+
const expression = unwrapParentheses(condition);
|
|
1410
|
+
if (ts.isBinaryExpression(expression) &&
|
|
1411
|
+
expression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
|
|
1412
|
+
return [
|
|
1413
|
+
...getTypedArrayBoundsFactsFromCondition(expression.left, indexSymbol, checker),
|
|
1414
|
+
...getTypedArrayBoundsFactsFromCondition(expression.right, indexSymbol, checker)
|
|
1415
|
+
];
|
|
1416
|
+
}
|
|
1417
|
+
const fact = getTypedArrayBoundsFactFromComparison(expression, indexSymbol, checker);
|
|
1418
|
+
return fact === undefined ? [] : [fact];
|
|
1419
|
+
}
|
|
1420
|
+
function getTypedArrayBoundsFactFromComparison(expression, indexSymbol, checker) {
|
|
1421
|
+
if (!ts.isBinaryExpression(expression)) {
|
|
1422
|
+
return undefined;
|
|
1423
|
+
}
|
|
1424
|
+
if (expression.operatorToken.kind === ts.SyntaxKind.LessThanToken) {
|
|
1425
|
+
return createTypedArrayBoundsFact(expression.left, expression.right, indexSymbol, checker);
|
|
1426
|
+
}
|
|
1427
|
+
if (expression.operatorToken.kind === ts.SyntaxKind.GreaterThanToken) {
|
|
1428
|
+
return createTypedArrayBoundsFact(expression.right, expression.left, indexSymbol, checker);
|
|
1429
|
+
}
|
|
1430
|
+
return undefined;
|
|
1431
|
+
}
|
|
1432
|
+
function createTypedArrayBoundsFact(indexExpression, lengthExpression, indexSymbol, checker) {
|
|
1433
|
+
const maxOffset = getIndexOffset(indexExpression, indexSymbol, checker);
|
|
1434
|
+
const lengthAccess = getTypedArrayLengthAccess(lengthExpression, checker);
|
|
1435
|
+
if (maxOffset === undefined || lengthAccess === undefined) {
|
|
1436
|
+
return undefined;
|
|
1437
|
+
}
|
|
1438
|
+
return {
|
|
1439
|
+
arraySymbol: lengthAccess.arraySymbol,
|
|
1440
|
+
domain: lengthAccess.domain,
|
|
1441
|
+
indexSymbol,
|
|
1442
|
+
maxOffset
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
function getTypedArrayLengthAccess(expression, checker) {
|
|
1446
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1447
|
+
if (!ts.isPropertyAccessExpression(unwrappedExpression) ||
|
|
1448
|
+
unwrappedExpression.name.text !== "length" ||
|
|
1449
|
+
!ts.isIdentifier(unwrappedExpression.expression)) {
|
|
1450
|
+
return undefined;
|
|
1451
|
+
}
|
|
1452
|
+
const domain = getTypedArrayElementDomain(checker.getTypeAtLocation(unwrappedExpression.expression));
|
|
1453
|
+
const arraySymbol = checker.getSymbolAtLocation(unwrappedExpression.expression);
|
|
1454
|
+
if (domain === undefined || arraySymbol === undefined) {
|
|
1455
|
+
return undefined;
|
|
1456
|
+
}
|
|
1457
|
+
return {
|
|
1458
|
+
arraySymbol,
|
|
1459
|
+
domain
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
function getTypedArrayElementDomain(type) {
|
|
1463
|
+
const symbol = type.getSymbol() ?? type.aliasSymbol;
|
|
1464
|
+
const name = symbol === undefined ? undefined : symbol.getName();
|
|
1465
|
+
if (name === undefined) {
|
|
1466
|
+
return undefined;
|
|
1467
|
+
}
|
|
1468
|
+
switch (name) {
|
|
1469
|
+
case "Int8Array":
|
|
1470
|
+
case "Int16Array":
|
|
1471
|
+
case "Int32Array":
|
|
1472
|
+
return "i32";
|
|
1473
|
+
case "Uint8Array":
|
|
1474
|
+
case "Uint8ClampedArray":
|
|
1475
|
+
case "Uint16Array":
|
|
1476
|
+
case "Uint32Array":
|
|
1477
|
+
return "u32";
|
|
1478
|
+
case "Float32Array":
|
|
1479
|
+
return "f32";
|
|
1480
|
+
case "Float64Array":
|
|
1481
|
+
return "f64";
|
|
1482
|
+
default:
|
|
1483
|
+
return undefined;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
function getIndexOffset(expression, indexSymbol, checker) {
|
|
1487
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1488
|
+
if (isIdentifierForSymbol(unwrappedExpression, indexSymbol, checker)) {
|
|
1489
|
+
return 0;
|
|
1490
|
+
}
|
|
1491
|
+
if (ts.isBinaryExpression(unwrappedExpression) &&
|
|
1492
|
+
unwrappedExpression.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
1493
|
+
if (isIdentifierForSymbol(unwrappedExpression.left, indexSymbol, checker)) {
|
|
1494
|
+
return getNonNegativeIntegerLiteralValue(unwrappedExpression.right);
|
|
1495
|
+
}
|
|
1496
|
+
if (isIdentifierForSymbol(unwrappedExpression.right, indexSymbol, checker)) {
|
|
1497
|
+
return getNonNegativeIntegerLiteralValue(unwrappedExpression.left);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return undefined;
|
|
1501
|
+
}
|
|
1502
|
+
function isIndexPlusPositiveInteger(expression, indexSymbol, checker) {
|
|
1503
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1504
|
+
return (ts.isBinaryExpression(unwrappedExpression) &&
|
|
1505
|
+
unwrappedExpression.operatorToken.kind === ts.SyntaxKind.PlusToken &&
|
|
1506
|
+
((isIdentifierForSymbol(unwrappedExpression.left, indexSymbol, checker) &&
|
|
1507
|
+
isPositiveIntegerExpression(unwrappedExpression.right)) ||
|
|
1508
|
+
(isIdentifierForSymbol(unwrappedExpression.right, indexSymbol, checker) &&
|
|
1509
|
+
isPositiveIntegerExpression(unwrappedExpression.left))));
|
|
1510
|
+
}
|
|
1511
|
+
function statementWritesAnySymbol(node, symbols, checker) {
|
|
1512
|
+
let writes = false;
|
|
1513
|
+
const visit = (current) => {
|
|
1514
|
+
if (writes || isNestedRuntimeBoundary(current)) {
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
if (ts.isBinaryExpression(current) && isAssignmentOperator(current.operatorToken.kind)) {
|
|
1518
|
+
if (assignmentTargetWritesAnySymbol(current.left, symbols, checker)) {
|
|
1519
|
+
writes = true;
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
if ((ts.isPrefixUnaryExpression(current) ||
|
|
1524
|
+
ts.isPostfixUnaryExpression(current)) &&
|
|
1525
|
+
(current.operator === ts.SyntaxKind.PlusPlusToken ||
|
|
1526
|
+
current.operator === ts.SyntaxKind.MinusMinusToken) &&
|
|
1527
|
+
assignmentTargetWritesAnySymbol(current.operand, symbols, checker)) {
|
|
1528
|
+
writes = true;
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
ts.forEachChild(current, visit);
|
|
1532
|
+
};
|
|
1533
|
+
visit(node);
|
|
1534
|
+
return writes;
|
|
1535
|
+
}
|
|
1536
|
+
function assignmentTargetWritesAnySymbol(expression, symbols, checker) {
|
|
1537
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1538
|
+
if (ts.isIdentifier(unwrappedExpression)) {
|
|
1539
|
+
const symbol = checker.getSymbolAtLocation(unwrappedExpression);
|
|
1540
|
+
return symbol !== undefined && symbols.has(symbol);
|
|
1541
|
+
}
|
|
1542
|
+
if (ts.isArrayLiteralExpression(unwrappedExpression)) {
|
|
1543
|
+
return unwrappedExpression.elements.some((element) => assignmentTargetWritesAnySymbol(element, symbols, checker));
|
|
1544
|
+
}
|
|
1545
|
+
if (ts.isObjectLiteralExpression(unwrappedExpression)) {
|
|
1546
|
+
return unwrappedExpression.properties.some((property) => {
|
|
1547
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
1548
|
+
const symbol = checker.getSymbolAtLocation(property.name);
|
|
1549
|
+
return symbol !== undefined && symbols.has(symbol);
|
|
1550
|
+
}
|
|
1551
|
+
return (ts.isPropertyAssignment(property) &&
|
|
1552
|
+
assignmentTargetWritesAnySymbol(property.initializer, symbols, checker));
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
function isAssignmentOperator(operator) {
|
|
1558
|
+
return (operator === ts.SyntaxKind.EqualsToken ||
|
|
1559
|
+
isCompoundAssignmentOperator(operator));
|
|
1560
|
+
}
|
|
1561
|
+
function isIdentifierForSymbol(expression, symbol, checker) {
|
|
1562
|
+
return (ts.isIdentifier(unwrapParentheses(expression)) &&
|
|
1563
|
+
checker.getSymbolAtLocation(unwrapParentheses(expression)) === symbol);
|
|
1564
|
+
}
|
|
1565
|
+
function isZeroNumberExpression(expression) {
|
|
1566
|
+
return getNonNegativeIntegerLiteralValue(expression) === 0;
|
|
1567
|
+
}
|
|
1568
|
+
function isPositiveIntegerExpression(expression) {
|
|
1569
|
+
const value = getNonNegativeIntegerLiteralValue(expression);
|
|
1570
|
+
return value !== undefined && value > 0;
|
|
1571
|
+
}
|
|
1572
|
+
function getNonNegativeIntegerLiteralValue(expression) {
|
|
1573
|
+
const unwrappedExpression = unwrapParentheses(expression);
|
|
1574
|
+
if (!ts.isNumericLiteral(unwrappedExpression)) {
|
|
1575
|
+
return undefined;
|
|
1576
|
+
}
|
|
1577
|
+
const value = Number(unwrappedExpression.text);
|
|
1578
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
1579
|
+
}
|
|
1580
|
+
function cloneFacts(facts) {
|
|
1581
|
+
return new Map(facts);
|
|
1582
|
+
}
|
|
1583
|
+
function copyFacts(source, target) {
|
|
1584
|
+
for (const [symbol, domain] of source) {
|
|
1585
|
+
target.set(symbol, domain);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
function joinFacts(left, right) {
|
|
1589
|
+
const joinedFacts = new Map();
|
|
1590
|
+
for (const [symbol, leftDomain] of left) {
|
|
1591
|
+
const rightDomain = right.get(symbol);
|
|
1592
|
+
if (rightDomain === leftDomain) {
|
|
1593
|
+
joinedFacts.set(symbol, leftDomain);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
return joinedFacts;
|
|
1597
|
+
}
|
|
1598
|
+
function joinAllFacts(factMaps) {
|
|
1599
|
+
const [firstFactMap, ...remainingFactMaps] = factMaps;
|
|
1600
|
+
if (firstFactMap === undefined) {
|
|
1601
|
+
return new Map();
|
|
1602
|
+
}
|
|
1603
|
+
let joinedFacts = cloneFacts(firstFactMap);
|
|
1604
|
+
for (const factMap of remainingFactMaps) {
|
|
1605
|
+
joinedFacts = joinFacts(joinedFacts, factMap);
|
|
1606
|
+
}
|
|
1607
|
+
return joinedFacts;
|
|
1608
|
+
}
|
|
1609
|
+
function collectCapturedMutableSymbols(node, checker) {
|
|
1610
|
+
const mutableLocalSymbols = new Set();
|
|
1611
|
+
collectParameterSymbols(node, checker, mutableLocalSymbols);
|
|
1612
|
+
if (node.body !== undefined) {
|
|
1613
|
+
collectMutableLocalSymbols(node.body, checker, mutableLocalSymbols);
|
|
1614
|
+
}
|
|
1615
|
+
if (mutableLocalSymbols.size === 0 || node.body === undefined) {
|
|
1616
|
+
return new Set();
|
|
1617
|
+
}
|
|
1618
|
+
const capturedSymbols = new Set();
|
|
1619
|
+
collectCapturesFromNestedFunctions(node.body, checker, mutableLocalSymbols, capturedSymbols);
|
|
1620
|
+
return capturedSymbols;
|
|
1621
|
+
}
|
|
1622
|
+
function collectParameterSymbols(node, checker, symbols) {
|
|
1623
|
+
for (const parameter of node.parameters) {
|
|
1624
|
+
collectBindingNameSymbols(parameter.name, checker, symbols);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
function collectMutableLocalSymbols(node, checker, symbols) {
|
|
1628
|
+
const visit = (current) => {
|
|
1629
|
+
if (isNestedRuntimeBoundary(current)) {
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
if (ts.isVariableDeclarationList(current)) {
|
|
1633
|
+
if ((ts.getCombinedNodeFlags(current) & ts.NodeFlags.Const) === 0) {
|
|
1634
|
+
for (const declaration of current.declarations) {
|
|
1635
|
+
collectBindingNameSymbols(declaration.name, checker, symbols);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
ts.forEachChild(current, visit);
|
|
1641
|
+
};
|
|
1642
|
+
ts.forEachChild(node, visit);
|
|
1643
|
+
}
|
|
1644
|
+
function collectCapturesFromNestedFunctions(node, checker, mutableLocalSymbols, capturedSymbols) {
|
|
1645
|
+
const visit = (current) => {
|
|
1646
|
+
if (isFunctionLikeWithBody(current)) {
|
|
1647
|
+
const body = current.body;
|
|
1648
|
+
if (body !== undefined) {
|
|
1649
|
+
collectIdentifierCaptures(body, checker, mutableLocalSymbols, capturedSymbols);
|
|
1650
|
+
}
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
if (ts.isClassLike(current)) {
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
ts.forEachChild(current, visit);
|
|
1657
|
+
};
|
|
1658
|
+
ts.forEachChild(node, visit);
|
|
1659
|
+
}
|
|
1660
|
+
function collectIdentifierCaptures(node, checker, mutableLocalSymbols, capturedSymbols) {
|
|
1661
|
+
const visit = (current) => {
|
|
1662
|
+
if (ts.isTypeNode(current)) {
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
if (ts.isIdentifier(current)) {
|
|
1666
|
+
const symbol = checker.getSymbolAtLocation(current);
|
|
1667
|
+
if (symbol !== undefined && mutableLocalSymbols.has(symbol)) {
|
|
1668
|
+
capturedSymbols.add(symbol);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
ts.forEachChild(current, visit);
|
|
1672
|
+
};
|
|
1673
|
+
visit(node);
|
|
1674
|
+
}
|
|
1675
|
+
function collectBindingNameSymbols(name, checker, symbols) {
|
|
1676
|
+
if (ts.isIdentifier(name)) {
|
|
1677
|
+
const symbol = checker.getSymbolAtLocation(name);
|
|
1678
|
+
if (symbol !== undefined) {
|
|
1679
|
+
symbols.add(symbol);
|
|
1680
|
+
}
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
for (const element of name.elements) {
|
|
1684
|
+
if (ts.isBindingElement(element)) {
|
|
1685
|
+
collectBindingNameSymbols(element.name, checker, symbols);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
function isNestedRuntimeBoundary(node) {
|
|
1690
|
+
return isFunctionLikeWithBody(node) || ts.isClassLike(node);
|
|
1691
|
+
}
|
|
1692
|
+
function isFunctionLikeWithBody(node) {
|
|
1693
|
+
return ((ts.isArrowFunction(node) ||
|
|
1694
|
+
ts.isConstructorDeclaration(node) ||
|
|
1695
|
+
ts.isFunctionDeclaration(node) ||
|
|
1696
|
+
ts.isFunctionExpression(node) ||
|
|
1697
|
+
ts.isGetAccessorDeclaration(node) ||
|
|
1698
|
+
ts.isMethodDeclaration(node) ||
|
|
1699
|
+
ts.isSetAccessorDeclaration(node)) &&
|
|
1700
|
+
node.body !== undefined);
|
|
1701
|
+
}
|
|
1702
|
+
//# sourceMappingURL=optimize-coercions.js.map
|