storm-lua-minify 0.3.0 → 0.9.1

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.
Files changed (44) hide show
  1. package/README.md +119 -41
  2. package/dist/aggregateSpecialization.js +406 -0
  3. package/dist/ast2lua.js +156 -68
  4. package/dist/astWalk.js +162 -0
  5. package/dist/callGraph.js +372 -0
  6. package/dist/cli.js +53 -58
  7. package/dist/cliOptions.js +36 -0
  8. package/dist/cliProgress.js +87 -0
  9. package/dist/config.js +73 -0
  10. package/dist/constantFold.js +798 -0
  11. package/dist/controlFlow.js +266 -0
  12. package/dist/functionRewrites.js +580 -0
  13. package/dist/generatedAst.js +108 -0
  14. package/dist/generatedNode.js +23 -0
  15. package/dist/globalRename.js +17 -4
  16. package/dist/interproceduralAnalysis.js +842 -0
  17. package/dist/interproceduralConstants.js +120 -0
  18. package/dist/luaString.js +157 -0
  19. package/dist/minifier.js +1219 -49
  20. package/dist/optimizerAnalysis.js +43 -0
  21. package/dist/optimizerDiagnostics.js +65 -0
  22. package/dist/optimizerFacts.js +529 -0
  23. package/dist/optimizerPass.js +96 -0
  24. package/dist/optimizerTransaction.js +56 -0
  25. package/dist/optimizerValueDomain.js +200 -0
  26. package/dist/options.js +233 -0
  27. package/dist/progress.js +2 -0
  28. package/dist/removeUnused.js +145 -0
  29. package/dist/renamer.js +280 -54
  30. package/dist/resolver.js +35 -11
  31. package/dist/runtimeEnvironment.js +105 -0
  32. package/dist/sourceMetadata.js +314 -0
  33. package/dist/statementDataflow.js +259 -0
  34. package/dist/statementScheduler.js +598 -0
  35. package/dist/symbolLiveness.js +92 -0
  36. package/dist/tableEffects.js +356 -0
  37. package/dist/transform.js +10 -371
  38. package/dist/valueFlow.js +409 -0
  39. package/dist/wholeProgramExports.js +646 -0
  40. package/dist/wholeProgramFieldRenames.js +583 -0
  41. package/dist/wholeProgramFields.js +672 -0
  42. package/dist/wholeProgramObjects.js +783 -0
  43. package/package.json +11 -2
  44. 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
+ }