storm-lua-minify 0.3.0 → 0.9.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/README.md +119 -41
- package/dist/aggregateSpecialization.js +406 -0
- package/dist/ast2lua.js +156 -68
- package/dist/astWalk.js +162 -0
- package/dist/callGraph.js +372 -0
- package/dist/cli.js +53 -58
- package/dist/cliOptions.js +36 -0
- package/dist/cliProgress.js +87 -0
- package/dist/config.js +73 -0
- package/dist/constantFold.js +798 -0
- package/dist/controlFlow.js +266 -0
- package/dist/functionRewrites.js +580 -0
- package/dist/generatedAst.js +108 -0
- package/dist/generatedNode.js +23 -0
- package/dist/interproceduralAnalysis.js +842 -0
- package/dist/interproceduralConstants.js +120 -0
- package/dist/luaString.js +157 -0
- package/dist/minifier.js +1178 -44
- package/dist/optimizerAnalysis.js +43 -0
- package/dist/optimizerDiagnostics.js +65 -0
- package/dist/optimizerFacts.js +529 -0
- package/dist/optimizerPass.js +96 -0
- package/dist/optimizerTransaction.js +56 -0
- package/dist/optimizerValueDomain.js +180 -0
- package/dist/options.js +233 -0
- package/dist/progress.js +2 -0
- package/dist/removeUnused.js +145 -0
- package/dist/renamer.js +223 -54
- package/dist/resolver.js +28 -11
- package/dist/runtimeEnvironment.js +105 -0
- package/dist/sourceMetadata.js +314 -0
- package/dist/statementDataflow.js +259 -0
- package/dist/statementScheduler.js +595 -0
- package/dist/symbolLiveness.js +92 -0
- package/dist/tableEffects.js +356 -0
- package/dist/transform.js +10 -371
- package/dist/valueFlow.js +409 -0
- package/dist/wholeProgramExports.js +646 -0
- package/dist/wholeProgramFieldRenames.js +583 -0
- package/dist/wholeProgramFields.js +672 -0
- package/dist/wholeProgramObjects.js +783 -0
- package/package.json +11 -2
- package/dist/index.js +0 -27
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeTableEffects = analyzeTableEffects;
|
|
4
|
+
/** fresh local tableに対する構文事実を収集する。ASTは変更しない。 */
|
|
5
|
+
function analyzeTableEffects(chunk, resolved, valueFlow, facts, interprocedural) {
|
|
6
|
+
const freshTables = [];
|
|
7
|
+
const freshByAllocation = new Map();
|
|
8
|
+
const allocationByStableSymbol = new Map();
|
|
9
|
+
const effects = [];
|
|
10
|
+
const escapes = [];
|
|
11
|
+
if (valueFlow.version !== facts.generation) {
|
|
12
|
+
throw new Error("Table effects require facts from the same AST generation");
|
|
13
|
+
}
|
|
14
|
+
valueFlow.allocations.forEach((allocation) => {
|
|
15
|
+
const binding = bindingOfAllocation(allocation);
|
|
16
|
+
if (!binding)
|
|
17
|
+
return;
|
|
18
|
+
const table = {
|
|
19
|
+
allocation,
|
|
20
|
+
symbol: binding.symbol,
|
|
21
|
+
declaration: binding.declaration,
|
|
22
|
+
...(allocation.origin.type === "TableConstructorExpression"
|
|
23
|
+
? { constructor: allocation.origin }
|
|
24
|
+
: {}),
|
|
25
|
+
functionDepth: depthOf(allocation.unit),
|
|
26
|
+
};
|
|
27
|
+
freshTables.push(table);
|
|
28
|
+
freshByAllocation.set(allocation, table);
|
|
29
|
+
});
|
|
30
|
+
valueFlow.definitions.forEach((definition) => {
|
|
31
|
+
if (definition.value.kind !== "allocations" ||
|
|
32
|
+
definition.value.allocations.size !== 1) {
|
|
33
|
+
allocationByStableSymbol.set(definition.symbol, null);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const allocation = definition.value.allocations.values().next().value;
|
|
37
|
+
if (!allocation)
|
|
38
|
+
return;
|
|
39
|
+
const existing = allocationByStableSymbol.get(definition.symbol);
|
|
40
|
+
allocationByStableSymbol.set(definition.symbol, existing === undefined || existing === allocation ? allocation : null);
|
|
41
|
+
});
|
|
42
|
+
analyzeBlock(chunk.body, 0);
|
|
43
|
+
function escapeIdentifier(identifier, use, owner, functionDepth) {
|
|
44
|
+
const symbol = resolved.symbolOf(identifier);
|
|
45
|
+
const point = valueFlow.controlFlow.pointOf(owner);
|
|
46
|
+
const allocation = symbol && point
|
|
47
|
+
? valueFlow.allocationOfBase(identifier, point)
|
|
48
|
+
: undefined;
|
|
49
|
+
const fallback = symbol ? allocationByStableSymbol.get(symbol) : undefined;
|
|
50
|
+
const resolvedAllocation = allocation ?? fallback ?? undefined;
|
|
51
|
+
const table = resolvedAllocation
|
|
52
|
+
? freshByAllocation.get(resolvedAllocation)
|
|
53
|
+
: undefined;
|
|
54
|
+
if (!table)
|
|
55
|
+
return;
|
|
56
|
+
escapes.push({
|
|
57
|
+
table,
|
|
58
|
+
reason: functionDepth > table.functionDepth ? "capture" : use,
|
|
59
|
+
identifier,
|
|
60
|
+
owner,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function analyzeExpression(expression, owner, use, functionDepth) {
|
|
64
|
+
switch (expression.type) {
|
|
65
|
+
case "Identifier":
|
|
66
|
+
escapeIdentifier(expression, use, owner, functionDepth);
|
|
67
|
+
return;
|
|
68
|
+
case "StringLiteral":
|
|
69
|
+
case "NumericLiteral":
|
|
70
|
+
case "BooleanLiteral":
|
|
71
|
+
case "NilLiteral":
|
|
72
|
+
case "VarargLiteral":
|
|
73
|
+
return;
|
|
74
|
+
case "LogicalExpression":
|
|
75
|
+
case "BinaryExpression":
|
|
76
|
+
analyzeExpression(expression.left, owner, "value-use", functionDepth);
|
|
77
|
+
analyzeExpression(expression.right, owner, "value-use", functionDepth);
|
|
78
|
+
return;
|
|
79
|
+
case "UnaryExpression":
|
|
80
|
+
analyzeExpression(expression.argument, owner, "value-use", functionDepth);
|
|
81
|
+
return;
|
|
82
|
+
case "MemberExpression":
|
|
83
|
+
case "IndexExpression":
|
|
84
|
+
analyzeTableAccess(expression, "read", owner, functionDepth);
|
|
85
|
+
return;
|
|
86
|
+
case "CallExpression":
|
|
87
|
+
analyzeCall(expression, owner, functionDepth);
|
|
88
|
+
return;
|
|
89
|
+
case "TableCallExpression":
|
|
90
|
+
analyzeCall(expression, owner, functionDepth);
|
|
91
|
+
return;
|
|
92
|
+
case "StringCallExpression":
|
|
93
|
+
analyzeCall(expression, owner, functionDepth);
|
|
94
|
+
return;
|
|
95
|
+
case "FunctionDeclaration":
|
|
96
|
+
analyzeBlock(expression.body, functionDepth + 1);
|
|
97
|
+
return;
|
|
98
|
+
case "TableConstructorExpression":
|
|
99
|
+
expression.fields.forEach((field) => {
|
|
100
|
+
if (field.type === "TableKey") {
|
|
101
|
+
analyzeExpression(field.key, owner, "value-use", functionDepth);
|
|
102
|
+
}
|
|
103
|
+
analyzeExpression(field.value, owner, "store", functionDepth);
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
default: {
|
|
107
|
+
const exhaustive = expression;
|
|
108
|
+
throw new TypeError("Unknown expression type: `" + JSON.stringify(exhaustive) + "`");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function analyzeCall(call, owner, functionDepth) {
|
|
113
|
+
analyzeExpression(call.base, owner, "call", functionDepth);
|
|
114
|
+
const explicitArgs = call.type === "CallExpression"
|
|
115
|
+
? call.arguments
|
|
116
|
+
: [
|
|
117
|
+
call.type === "TableCallExpression"
|
|
118
|
+
? call.arguments
|
|
119
|
+
: call.argument,
|
|
120
|
+
];
|
|
121
|
+
const args = call.base.type === "MemberExpression" && call.base.indexer === ":"
|
|
122
|
+
? [call.base.base, ...explicitArgs]
|
|
123
|
+
: explicitArgs;
|
|
124
|
+
const callSite = interprocedural?.callGraph.callSiteOf(call);
|
|
125
|
+
args.forEach((argument, argumentIndex) => {
|
|
126
|
+
const symbol = argument.type === "Identifier"
|
|
127
|
+
? resolved.symbolOf(argument)
|
|
128
|
+
: undefined;
|
|
129
|
+
const table = tableOfBase(argument, owner);
|
|
130
|
+
if (table && symbol && callSite && interprocedural) {
|
|
131
|
+
interprocedural
|
|
132
|
+
.effectsOf(callSite)
|
|
133
|
+
.filter((effect) => effect.argumentIndex === argumentIndex)
|
|
134
|
+
.forEach((effect) => effects.push({
|
|
135
|
+
access: effect.access,
|
|
136
|
+
table,
|
|
137
|
+
staticKey: effect.staticKey,
|
|
138
|
+
owner,
|
|
139
|
+
baseSymbol: symbol,
|
|
140
|
+
}));
|
|
141
|
+
if (!interprocedural.escapesArgument(callSite, argumentIndex))
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
analyzeExpression(argument, owner, "call", functionDepth);
|
|
145
|
+
});
|
|
146
|
+
if (call.base.type === "MemberExpression" &&
|
|
147
|
+
call.base.indexer === ":" &&
|
|
148
|
+
!callSite) {
|
|
149
|
+
const table = tableOfBase(call.base.base, owner);
|
|
150
|
+
if (table && call.base.base.type === "Identifier") {
|
|
151
|
+
escapeIdentifier(call.base.base, "call", owner, functionDepth);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function analyzeTableAccess(expression, access, owner, functionDepth) {
|
|
156
|
+
const baseSymbol = expression.base.type === "Identifier"
|
|
157
|
+
? resolved.symbolOf(expression.base)
|
|
158
|
+
: undefined;
|
|
159
|
+
const table = tableOfBase(expression.base, owner);
|
|
160
|
+
if (table && baseSymbol) {
|
|
161
|
+
effects.push({
|
|
162
|
+
access,
|
|
163
|
+
table,
|
|
164
|
+
staticKey: staticKeyFromFacts(expression),
|
|
165
|
+
expression,
|
|
166
|
+
owner,
|
|
167
|
+
baseSymbol,
|
|
168
|
+
});
|
|
169
|
+
if (functionDepth > table.functionDepth &&
|
|
170
|
+
expression.base.type === "Identifier") {
|
|
171
|
+
escapeIdentifier(expression.base, "value-use", owner, functionDepth);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
analyzeExpression(expression.base, owner, "value-use", functionDepth);
|
|
176
|
+
}
|
|
177
|
+
if (expression.type === "IndexExpression") {
|
|
178
|
+
analyzeExpression(expression.index, owner, "value-use", functionDepth);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function staticKeyFromFacts(expression) {
|
|
182
|
+
const operation = facts.operationOf(expression);
|
|
183
|
+
if (!operation ||
|
|
184
|
+
(operation.kind !== "table-read" && operation.kind !== "table-write")) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
return operation.location.key.kind === "static"
|
|
188
|
+
? operation.location.key.value
|
|
189
|
+
: undefined;
|
|
190
|
+
}
|
|
191
|
+
function tableOfBase(base, owner) {
|
|
192
|
+
const point = valueFlow.controlFlow.pointOf(owner);
|
|
193
|
+
if (!point)
|
|
194
|
+
return undefined;
|
|
195
|
+
const allocation = valueFlow.allocationOfBase(base, point);
|
|
196
|
+
return allocation ? freshByAllocation.get(allocation) : undefined;
|
|
197
|
+
}
|
|
198
|
+
function isTrackedLocalAlias(expression, owner, functionDepth) {
|
|
199
|
+
if (expression.type !== "Identifier")
|
|
200
|
+
return false;
|
|
201
|
+
const point = valueFlow.controlFlow.pointOf(owner);
|
|
202
|
+
if (!point)
|
|
203
|
+
return false;
|
|
204
|
+
const allocation = valueFlow.allocationOfBase(expression, point);
|
|
205
|
+
const table = allocation ? freshByAllocation.get(allocation) : undefined;
|
|
206
|
+
return table?.functionDepth === functionDepth;
|
|
207
|
+
}
|
|
208
|
+
function analyzeStatement(statement, functionDepth) {
|
|
209
|
+
switch (statement.type) {
|
|
210
|
+
case "LocalStatement":
|
|
211
|
+
statement.init.forEach((expression) => {
|
|
212
|
+
if (!isTrackedLocalAlias(expression, statement, functionDepth)) {
|
|
213
|
+
analyzeExpression(expression, statement, "alias", functionDepth);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
case "AssignmentStatement":
|
|
218
|
+
statement.init.forEach((expression) => {
|
|
219
|
+
// 既存bindingへの代入はupvalueへの公開になり得る。代入先localの
|
|
220
|
+
// execution unitを証明する解析が入るまでは、単純aliasでもescape扱い。
|
|
221
|
+
analyzeExpression(expression, statement, "store", functionDepth);
|
|
222
|
+
});
|
|
223
|
+
statement.variables.forEach((variable) => {
|
|
224
|
+
if (variable.type === "MemberExpression" ||
|
|
225
|
+
variable.type === "IndexExpression") {
|
|
226
|
+
analyzeTableAccess(variable, "write", statement, functionDepth);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
case "CallStatement":
|
|
231
|
+
analyzeExpression(statement.expression, statement, "call", functionDepth);
|
|
232
|
+
return;
|
|
233
|
+
case "DoStatement":
|
|
234
|
+
analyzeBlock(statement.body, functionDepth);
|
|
235
|
+
return;
|
|
236
|
+
case "WhileStatement":
|
|
237
|
+
analyzeExpression(statement.condition, statement, "value-use", functionDepth);
|
|
238
|
+
analyzeBlock(statement.body, functionDepth);
|
|
239
|
+
return;
|
|
240
|
+
case "RepeatStatement":
|
|
241
|
+
analyzeBlock(statement.body, functionDepth);
|
|
242
|
+
analyzeExpression(statement.condition, statement, "value-use", functionDepth);
|
|
243
|
+
return;
|
|
244
|
+
case "IfStatement":
|
|
245
|
+
statement.clauses.forEach((clause) => {
|
|
246
|
+
if (clause.type !== "ElseClause") {
|
|
247
|
+
analyzeExpression(clause.condition, statement, "value-use", functionDepth);
|
|
248
|
+
}
|
|
249
|
+
analyzeBlock(clause.body, functionDepth);
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
case "ForNumericStatement":
|
|
253
|
+
analyzeExpression(statement.start, statement, "value-use", functionDepth);
|
|
254
|
+
analyzeExpression(statement.end, statement, "value-use", functionDepth);
|
|
255
|
+
if (statement.step) {
|
|
256
|
+
analyzeExpression(statement.step, statement, "value-use", functionDepth);
|
|
257
|
+
}
|
|
258
|
+
analyzeBlock(statement.body, functionDepth);
|
|
259
|
+
return;
|
|
260
|
+
case "ForGenericStatement":
|
|
261
|
+
statement.iterators.forEach((iterator) => {
|
|
262
|
+
analyzeExpression(iterator, statement, "value-use", functionDepth);
|
|
263
|
+
});
|
|
264
|
+
analyzeBlock(statement.body, functionDepth);
|
|
265
|
+
return;
|
|
266
|
+
case "FunctionDeclaration":
|
|
267
|
+
if (statement.identifier &&
|
|
268
|
+
statement.identifier.type !== "Identifier") {
|
|
269
|
+
analyzeTableAccess(statement.identifier, "write", statement, functionDepth);
|
|
270
|
+
}
|
|
271
|
+
analyzeBlock(statement.body, functionDepth + 1);
|
|
272
|
+
return;
|
|
273
|
+
case "ReturnStatement":
|
|
274
|
+
statement.arguments.forEach((argument) => {
|
|
275
|
+
analyzeExpression(argument, statement, "return", functionDepth);
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
case "BreakStatement":
|
|
279
|
+
case "LabelStatement":
|
|
280
|
+
case "GotoStatement":
|
|
281
|
+
return;
|
|
282
|
+
default: {
|
|
283
|
+
const exhaustive = statement;
|
|
284
|
+
throw new TypeError("Unknown statement type: `" + JSON.stringify(exhaustive) + "`");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function analyzeBlock(body, functionDepth) {
|
|
289
|
+
body.forEach((statement) => {
|
|
290
|
+
analyzeStatement(statement, functionDepth);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
facts,
|
|
295
|
+
freshTables,
|
|
296
|
+
effects,
|
|
297
|
+
escapes,
|
|
298
|
+
isNonescaping: (table) => !escapes.some((escape) => escape.table === table),
|
|
299
|
+
effectsOf: (table) => effects.filter((effect) => effect.table === table),
|
|
300
|
+
escapeReasonsOf: (table) => escapes
|
|
301
|
+
.filter((escape) => escape.table === table)
|
|
302
|
+
.map((escape) => escape.reason),
|
|
303
|
+
stabilityBetween: (table, baseSymbol, first, last) => {
|
|
304
|
+
const firstPoint = valueFlow.controlFlow.pointOf(first);
|
|
305
|
+
const lastPoint = valueFlow.controlFlow.pointOf(last);
|
|
306
|
+
if (!firstPoint ||
|
|
307
|
+
!lastPoint ||
|
|
308
|
+
firstPoint.unit !== lastPoint.unit ||
|
|
309
|
+
!valueFlow.controlFlow.dominates(first, last)) {
|
|
310
|
+
return { stable: false, reason: "control-flow-barrier" };
|
|
311
|
+
}
|
|
312
|
+
return valueFlow.stableAllocationBetween(first, last, baseSymbol, table.allocation)
|
|
313
|
+
? { stable: true }
|
|
314
|
+
: { stable: false, reason: "unstable-reaching-definition" };
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
function bindingOfAllocation(allocation) {
|
|
318
|
+
const owner = allocation.owner;
|
|
319
|
+
if (owner.type !== "LocalStatement" &&
|
|
320
|
+
owner.type !== "AssignmentStatement") {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
const index = owner.init.indexOf(allocation.origin);
|
|
324
|
+
if (index < 0)
|
|
325
|
+
return undefined;
|
|
326
|
+
const target = owner.variables[index];
|
|
327
|
+
if (target.type !== "Identifier")
|
|
328
|
+
return undefined;
|
|
329
|
+
const symbol = resolved.symbolOf(target);
|
|
330
|
+
if (!symbol)
|
|
331
|
+
return undefined;
|
|
332
|
+
if (owner.type === "AssignmentStatement") {
|
|
333
|
+
const declaration = facts
|
|
334
|
+
.operationsOfSymbol(symbol)
|
|
335
|
+
.find((operation) => operation.kind === "declare" &&
|
|
336
|
+
operation.origin === symbol.declaration);
|
|
337
|
+
const declarationPoint = declaration
|
|
338
|
+
? valueFlow.controlFlow.pointOf(declaration.owner)
|
|
339
|
+
: undefined;
|
|
340
|
+
const allocationPoint = valueFlow.controlFlow.pointOf(owner);
|
|
341
|
+
if (!declarationPoint ||
|
|
342
|
+
!allocationPoint ||
|
|
343
|
+
declarationPoint.unit !== allocation.unit ||
|
|
344
|
+
allocationPoint.unit !== allocation.unit ||
|
|
345
|
+
!valueFlow.controlFlow.dominates(declarationPoint.statement, allocationPoint.statement))
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
return { symbol, declaration: owner };
|
|
349
|
+
}
|
|
350
|
+
function depthOf(unit) {
|
|
351
|
+
let depth = 0;
|
|
352
|
+
for (let current = unit.parent; current; current = current.parent)
|
|
353
|
+
depth++;
|
|
354
|
+
return depth;
|
|
355
|
+
}
|
|
356
|
+
}
|