@markw65/monkeyc-optimizer 1.0.27 → 1.0.30
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 +45 -0
- package/build/api.cjs +2087 -226
- package/build/optimizer.cjs +2010 -172
- package/build/src/api.d.ts +2 -4
- package/build/src/ast.d.ts +7 -0
- package/build/src/control-flow.d.ts +20 -0
- package/build/src/inliner.d.ts +3 -3
- package/build/src/mc-rewrite.d.ts +1 -1
- package/build/src/optimizer.d.ts +1 -0
- package/package.json +4 -2
package/build/api.cjs
CHANGED
|
@@ -1,8 +1,211 @@
|
|
|
1
1
|
0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiMapping,hasProperty,isStateNode,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
|
|
2
2
|
/******/ (() => { // webpackBootstrap
|
|
3
|
-
/******/
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/******/ var __webpack_modules__ = ({
|
|
4
|
+
|
|
5
|
+
/***/ 2789:
|
|
6
|
+
/***/ ((module) => {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Expose `PriorityQueue`.
|
|
10
|
+
*/
|
|
11
|
+
module.exports = PriorityQueue;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initializes a new empty `PriorityQueue` with the given `comparator(a, b)`
|
|
15
|
+
* function, uses `.DEFAULT_COMPARATOR()` when no function is provided.
|
|
16
|
+
*
|
|
17
|
+
* The comparator function must return a positive number when `a > b`, 0 when
|
|
18
|
+
* `a == b` and a negative number when `a < b`.
|
|
19
|
+
*
|
|
20
|
+
* @param {Function}
|
|
21
|
+
* @return {PriorityQueue}
|
|
22
|
+
* @api public
|
|
23
|
+
*/
|
|
24
|
+
function PriorityQueue(comparator) {
|
|
25
|
+
this._comparator = comparator || PriorityQueue.DEFAULT_COMPARATOR;
|
|
26
|
+
this._elements = [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compares `a` and `b`, when `a > b` it returns a positive number, when
|
|
31
|
+
* it returns 0 and when `a < b` it returns a negative number.
|
|
32
|
+
*
|
|
33
|
+
* @param {String|Number} a
|
|
34
|
+
* @param {String|Number} b
|
|
35
|
+
* @return {Number}
|
|
36
|
+
* @api public
|
|
37
|
+
*/
|
|
38
|
+
PriorityQueue.DEFAULT_COMPARATOR = function(a, b) {
|
|
39
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
40
|
+
return a - b;
|
|
41
|
+
} else {
|
|
42
|
+
a = a.toString();
|
|
43
|
+
b = b.toString();
|
|
44
|
+
|
|
45
|
+
if (a == b) return 0;
|
|
46
|
+
|
|
47
|
+
return (a > b) ? 1 : -1;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns whether the priority queue is empty or not.
|
|
53
|
+
*
|
|
54
|
+
* @return {Boolean}
|
|
55
|
+
* @api public
|
|
56
|
+
*/
|
|
57
|
+
PriorityQueue.prototype.isEmpty = function() {
|
|
58
|
+
return this.size() === 0;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Peeks at the top element of the priority queue.
|
|
63
|
+
*
|
|
64
|
+
* @return {Object}
|
|
65
|
+
* @throws {Error} when the queue is empty.
|
|
66
|
+
* @api public
|
|
67
|
+
*/
|
|
68
|
+
PriorityQueue.prototype.peek = function() {
|
|
69
|
+
if (this.isEmpty()) throw new Error('PriorityQueue is empty');
|
|
70
|
+
|
|
71
|
+
return this._elements[0];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Dequeues the top element of the priority queue.
|
|
76
|
+
*
|
|
77
|
+
* @return {Object}
|
|
78
|
+
* @throws {Error} when the queue is empty.
|
|
79
|
+
* @api public
|
|
80
|
+
*/
|
|
81
|
+
PriorityQueue.prototype.deq = function() {
|
|
82
|
+
var first = this.peek();
|
|
83
|
+
var last = this._elements.pop();
|
|
84
|
+
var size = this.size();
|
|
85
|
+
|
|
86
|
+
if (size === 0) return first;
|
|
87
|
+
|
|
88
|
+
this._elements[0] = last;
|
|
89
|
+
var current = 0;
|
|
90
|
+
|
|
91
|
+
while (current < size) {
|
|
92
|
+
var largest = current;
|
|
93
|
+
var left = (2 * current) + 1;
|
|
94
|
+
var right = (2 * current) + 2;
|
|
95
|
+
|
|
96
|
+
if (left < size && this._compare(left, largest) >= 0) {
|
|
97
|
+
largest = left;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (right < size && this._compare(right, largest) >= 0) {
|
|
101
|
+
largest = right;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (largest === current) break;
|
|
105
|
+
|
|
106
|
+
this._swap(largest, current);
|
|
107
|
+
current = largest;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return first;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Enqueues the `element` at the priority queue and returns its new size.
|
|
115
|
+
*
|
|
116
|
+
* @param {Object} element
|
|
117
|
+
* @return {Number}
|
|
118
|
+
* @api public
|
|
119
|
+
*/
|
|
120
|
+
PriorityQueue.prototype.enq = function(element) {
|
|
121
|
+
var size = this._elements.push(element);
|
|
122
|
+
var current = size - 1;
|
|
123
|
+
|
|
124
|
+
while (current > 0) {
|
|
125
|
+
var parent = Math.floor((current - 1) / 2);
|
|
126
|
+
|
|
127
|
+
if (this._compare(current, parent) <= 0) break;
|
|
128
|
+
|
|
129
|
+
this._swap(parent, current);
|
|
130
|
+
current = parent;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return size;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Returns the size of the priority queue.
|
|
138
|
+
*
|
|
139
|
+
* @return {Number}
|
|
140
|
+
* @api public
|
|
141
|
+
*/
|
|
142
|
+
PriorityQueue.prototype.size = function() {
|
|
143
|
+
return this._elements.length;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Iterates over queue elements
|
|
148
|
+
*
|
|
149
|
+
* @param {Function} fn
|
|
150
|
+
*/
|
|
151
|
+
PriorityQueue.prototype.forEach = function(fn) {
|
|
152
|
+
return this._elements.forEach(fn);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Compares the values at position `a` and `b` in the priority queue using its
|
|
157
|
+
* comparator function.
|
|
158
|
+
*
|
|
159
|
+
* @param {Number} a
|
|
160
|
+
* @param {Number} b
|
|
161
|
+
* @return {Number}
|
|
162
|
+
* @api private
|
|
163
|
+
*/
|
|
164
|
+
PriorityQueue.prototype._compare = function(a, b) {
|
|
165
|
+
return this._comparator(this._elements[a], this._elements[b]);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Swaps the values at position `a` and `b` in the priority queue.
|
|
170
|
+
*
|
|
171
|
+
* @param {Number} a
|
|
172
|
+
* @param {Number} b
|
|
173
|
+
* @api private
|
|
174
|
+
*/
|
|
175
|
+
PriorityQueue.prototype._swap = function(a, b) {
|
|
176
|
+
var aux = this._elements[a];
|
|
177
|
+
this._elements[a] = this._elements[b];
|
|
178
|
+
this._elements[b] = aux;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
/***/ })
|
|
183
|
+
|
|
184
|
+
/******/ });
|
|
185
|
+
/************************************************************************/
|
|
186
|
+
/******/ // The module cache
|
|
187
|
+
/******/ var __webpack_module_cache__ = {};
|
|
188
|
+
/******/
|
|
189
|
+
/******/ // The require function
|
|
190
|
+
/******/ function __webpack_require__(moduleId) {
|
|
191
|
+
/******/ // Check if module is in cache
|
|
192
|
+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
193
|
+
/******/ if (cachedModule !== undefined) {
|
|
194
|
+
/******/ return cachedModule.exports;
|
|
195
|
+
/******/ }
|
|
196
|
+
/******/ // Create a new module (and put it into the cache)
|
|
197
|
+
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
198
|
+
/******/ // no module.id needed
|
|
199
|
+
/******/ // no module.loaded needed
|
|
200
|
+
/******/ exports: {}
|
|
201
|
+
/******/ };
|
|
202
|
+
/******/
|
|
203
|
+
/******/ // Execute the module function
|
|
204
|
+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
205
|
+
/******/
|
|
206
|
+
/******/ // Return the exports of the module
|
|
207
|
+
/******/ return module.exports;
|
|
208
|
+
/******/ }
|
|
6
209
|
/******/
|
|
7
210
|
/************************************************************************/
|
|
8
211
|
/******/ /* webpack/runtime/compat get default export */
|
|
@@ -47,6 +250,9 @@
|
|
|
47
250
|
/******/
|
|
48
251
|
/************************************************************************/
|
|
49
252
|
var __webpack_exports__ = {};
|
|
253
|
+
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
|
|
254
|
+
(() => {
|
|
255
|
+
"use strict";
|
|
50
256
|
// ESM COMPAT FLAG
|
|
51
257
|
__webpack_require__.r(__webpack_exports__);
|
|
52
258
|
|
|
@@ -56,7 +262,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
56
262
|
"findUsingForNode": () => (/* binding */ findUsingForNode),
|
|
57
263
|
"formatAst": () => (/* binding */ api_formatAst),
|
|
58
264
|
"getApiMapping": () => (/* binding */ api_getApiMapping),
|
|
59
|
-
"hasProperty": () => (/*
|
|
265
|
+
"hasProperty": () => (/* reexport */ ast_hasProperty),
|
|
60
266
|
"isStateNode": () => (/* binding */ api_isStateNode),
|
|
61
267
|
"sameLookupResult": () => (/* binding */ api_sameLookupResult),
|
|
62
268
|
"traverseAst": () => (/* reexport */ ast_traverseAst),
|
|
@@ -86,62 +292,68 @@ function _check(x) {
|
|
|
86
292
|
x = y;
|
|
87
293
|
}
|
|
88
294
|
const mctreeTypeInfo = {
|
|
89
|
-
ArrayExpression: ["elements"],
|
|
90
|
-
AssignmentExpression: ["left", "right"],
|
|
91
|
-
AttributeList: ["attributes"],
|
|
92
|
-
Attributes: ["elements"],
|
|
93
|
-
BinaryExpression: ["left", "right"],
|
|
94
|
-
Block: [],
|
|
95
|
-
BlockStatement: ["body", "innerComments"],
|
|
96
|
-
BreakStatement: [],
|
|
97
|
-
CallExpression: ["callee", "arguments"],
|
|
98
|
-
CatchClause: ["param", "body"],
|
|
99
|
-
CatchClauses: ["catches"],
|
|
100
|
-
ClassBody: ["body"],
|
|
101
|
-
ClassDeclaration: ["attrs", "id", "superClass", "body"],
|
|
102
|
-
ClassElement: ["item"],
|
|
103
|
-
ConditionalExpression:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
295
|
+
ArrayExpression: { keys: ["elements"], expr: true },
|
|
296
|
+
AssignmentExpression: { keys: ["left", "right"], expr: true },
|
|
297
|
+
AttributeList: { keys: ["attributes"] },
|
|
298
|
+
Attributes: { keys: ["elements"] },
|
|
299
|
+
BinaryExpression: { keys: ["left", "right"], expr: true },
|
|
300
|
+
Block: { keys: [] },
|
|
301
|
+
BlockStatement: { keys: ["body", "innerComments"], stmt: true },
|
|
302
|
+
BreakStatement: { keys: [], stmt: true },
|
|
303
|
+
CallExpression: { keys: ["callee", "arguments"], expr: true },
|
|
304
|
+
CatchClause: { keys: ["param", "body"] },
|
|
305
|
+
CatchClauses: { keys: ["catches"] },
|
|
306
|
+
ClassBody: { keys: ["body"] },
|
|
307
|
+
ClassDeclaration: { keys: ["attrs", "id", "superClass", "body"], stmt: true },
|
|
308
|
+
ClassElement: { keys: ["item"] },
|
|
309
|
+
ConditionalExpression: {
|
|
310
|
+
keys: ["test", "consequent", "alternate"],
|
|
311
|
+
expr: true,
|
|
312
|
+
},
|
|
313
|
+
ContinueStatement: { keys: [], stmt: true },
|
|
314
|
+
DoWhileStatement: { keys: ["body", "test"], stmt: true },
|
|
315
|
+
EnumDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
|
|
316
|
+
EnumStringBody: { keys: ["members"] },
|
|
317
|
+
EnumStringMember: { keys: ["id", "init"] },
|
|
318
|
+
ExpressionStatement: { keys: ["expression"], stmt: true },
|
|
319
|
+
ForStatement: { keys: ["init", "test", "body", "update"], stmt: true },
|
|
320
|
+
FunctionDeclaration: {
|
|
321
|
+
keys: ["attrs", "id", "params", "returnType", "body"],
|
|
322
|
+
stmt: true,
|
|
323
|
+
},
|
|
324
|
+
Identifier: { keys: [], expr: true },
|
|
325
|
+
IfStatement: { keys: ["test", "consequent", "alternate"], stmt: true },
|
|
326
|
+
ImportModule: { keys: ["id"] },
|
|
327
|
+
InstanceOfCase: { keys: ["id"] },
|
|
328
|
+
Line: { keys: [] },
|
|
329
|
+
Literal: { keys: [], expr: true },
|
|
330
|
+
LogicalExpression: { keys: ["left", "right"], expr: true },
|
|
331
|
+
MemberExpression: { keys: ["object", "property"], expr: true },
|
|
332
|
+
MethodDefinition: { keys: ["params", "returnType"] },
|
|
333
|
+
ModuleDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
|
|
334
|
+
MultiLine: { keys: [] },
|
|
335
|
+
NewExpression: { keys: ["callee", "arguments"], expr: true },
|
|
336
|
+
ObjectExpression: { keys: ["properties"], expr: true },
|
|
337
|
+
ParenthesizedExpression: { keys: ["expression"], expr: true },
|
|
338
|
+
Program: { keys: ["body", "comments"] },
|
|
339
|
+
Property: { keys: ["key", "value"] },
|
|
340
|
+
ReturnStatement: { keys: ["argument"], stmt: true },
|
|
341
|
+
SequenceExpression: { keys: ["expressions"], expr: true },
|
|
342
|
+
SizedArrayExpression: { keys: ["size", "ts"], expr: true },
|
|
343
|
+
SwitchCase: { keys: ["test", "consequent"] },
|
|
344
|
+
SwitchStatement: { keys: ["discriminant", "cases"], stmt: true },
|
|
345
|
+
ThisExpression: { keys: [], expr: true },
|
|
346
|
+
ThrowStatement: { keys: ["argument"], stmt: true },
|
|
347
|
+
TryStatement: { keys: ["block", "handler", "finalizer"], stmt: true },
|
|
348
|
+
TypedefDeclaration: { keys: ["attrs", "id", "ts"], stmt: true },
|
|
349
|
+
TypeSpecList: { keys: ["ts"] },
|
|
350
|
+
TypeSpecPart: { keys: ["name", "body", "callspec", "generics"] },
|
|
351
|
+
UnaryExpression: { keys: ["argument"], expr: true },
|
|
352
|
+
UpdateExpression: { keys: ["argument"], expr: true },
|
|
353
|
+
Using: { keys: ["id", "as"] },
|
|
354
|
+
VariableDeclaration: { keys: ["attrs", "declarations"], stmt: true },
|
|
355
|
+
VariableDeclarator: { keys: ["id", "init"] },
|
|
356
|
+
WhileStatement: { keys: ["test", "body"], stmt: true },
|
|
145
357
|
};
|
|
146
358
|
function isMCTreeNode(node) {
|
|
147
359
|
return node ? typeof node === "object" && "type" in node : false;
|
|
@@ -166,7 +378,7 @@ function ast_traverseAst(node, pre, post) {
|
|
|
166
378
|
if (!mctreeTypeInfo[node.type]) {
|
|
167
379
|
throw new Error("what?");
|
|
168
380
|
}
|
|
169
|
-
for (const key of nodes || mctreeTypeInfo[node.type]) {
|
|
381
|
+
for (const key of nodes || mctreeTypeInfo[node.type].keys) {
|
|
170
382
|
const value = node[key];
|
|
171
383
|
if (!value)
|
|
172
384
|
continue;
|
|
@@ -193,13 +405,21 @@ function ast_traverseAst(node, pre, post) {
|
|
|
193
405
|
}
|
|
194
406
|
}
|
|
195
407
|
else if (isMCTreeNode(value)) {
|
|
196
|
-
|
|
408
|
+
let repl = ast_traverseAst(value, pre, post);
|
|
197
409
|
if (repl === false) {
|
|
198
410
|
delete node[key];
|
|
199
411
|
}
|
|
200
412
|
else if (repl != null) {
|
|
201
413
|
if (Array.isArray(repl)) {
|
|
202
|
-
|
|
414
|
+
if (ast_isStatement(value) && repl.every((s) => ast_isStatement(s))) {
|
|
415
|
+
repl = ast_withLoc({
|
|
416
|
+
type: "BlockStatement",
|
|
417
|
+
body: repl,
|
|
418
|
+
}, repl[0], repl[repl.length - 1]);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
throw new Error("Array returned by traverseAst in Node context");
|
|
422
|
+
}
|
|
203
423
|
}
|
|
204
424
|
node[key] = repl;
|
|
205
425
|
}
|
|
@@ -207,6 +427,55 @@ function ast_traverseAst(node, pre, post) {
|
|
|
207
427
|
}
|
|
208
428
|
return post && post(node);
|
|
209
429
|
}
|
|
430
|
+
function ast_isStatement(node) {
|
|
431
|
+
return ast_hasProperty(mctreeTypeInfo[node.type], "stmt");
|
|
432
|
+
}
|
|
433
|
+
function ast_isExpression(node) {
|
|
434
|
+
return ast_hasProperty(mctreeTypeInfo[node.type], "expr");
|
|
435
|
+
}
|
|
436
|
+
function ast_mayThrow(node) {
|
|
437
|
+
switch (node.type) {
|
|
438
|
+
case "BinaryExpression":
|
|
439
|
+
case "CallExpression":
|
|
440
|
+
case "ConditionalExpression":
|
|
441
|
+
case "LogicalExpression":
|
|
442
|
+
case "NewExpression":
|
|
443
|
+
case "ThrowStatement":
|
|
444
|
+
case "UnaryExpression":
|
|
445
|
+
case "UpdateExpression":
|
|
446
|
+
return true;
|
|
447
|
+
default:
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function ast_hasProperty(obj, prop) {
|
|
452
|
+
return obj ? Object.prototype.hasOwnProperty.call(obj, prop) : false;
|
|
453
|
+
}
|
|
454
|
+
function ast_withLoc(node, start, end) {
|
|
455
|
+
if (start && start.loc) {
|
|
456
|
+
node.start = start.start;
|
|
457
|
+
if (!node.end)
|
|
458
|
+
node.end = start.end;
|
|
459
|
+
node.loc = { ...(node.loc || start.loc), start: start.loc.start };
|
|
460
|
+
}
|
|
461
|
+
if (end && end.loc) {
|
|
462
|
+
node.end = end.end;
|
|
463
|
+
node.loc = { ...(node.loc || end.loc), end: end.loc.end };
|
|
464
|
+
}
|
|
465
|
+
return node;
|
|
466
|
+
}
|
|
467
|
+
function ast_withLocDeep(node, start, end) {
|
|
468
|
+
node = ast_withLoc({ ...node }, start, end);
|
|
469
|
+
for (const key of mctreeTypeInfo[node.type].keys) {
|
|
470
|
+
const value = node[key];
|
|
471
|
+
if (!value)
|
|
472
|
+
continue;
|
|
473
|
+
const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end) : v;
|
|
474
|
+
const repl = Array.isArray(value) ? value.map(fix) : fix(value);
|
|
475
|
+
node[key] = repl;
|
|
476
|
+
}
|
|
477
|
+
return node;
|
|
478
|
+
}
|
|
210
479
|
|
|
211
480
|
;// CONCATENATED MODULE: external "./api.cjs"
|
|
212
481
|
const external_api_cjs_namespaceObject = require("./api.cjs");
|
|
@@ -463,6 +732,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
463
732
|
const post = state.post;
|
|
464
733
|
state.inlining = true;
|
|
465
734
|
let insertedVariableDecls = null;
|
|
735
|
+
const replacements = new Set();
|
|
466
736
|
try {
|
|
467
737
|
state.pre = (node) => {
|
|
468
738
|
if (failed)
|
|
@@ -470,7 +740,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
470
740
|
node.start = call.start;
|
|
471
741
|
node.end = call.end;
|
|
472
742
|
node.loc = call.loc;
|
|
473
|
-
if (node
|
|
743
|
+
if (replacements.has(node))
|
|
474
744
|
return false;
|
|
475
745
|
const result = pre(node, state);
|
|
476
746
|
if (!insertedVariableDecls && node.type === "BlockStatement") {
|
|
@@ -498,6 +768,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
498
768
|
kind: "var",
|
|
499
769
|
};
|
|
500
770
|
node.body.unshift(insertedVariableDecls);
|
|
771
|
+
replacements.add(insertedVariableDecls);
|
|
501
772
|
}
|
|
502
773
|
return result;
|
|
503
774
|
};
|
|
@@ -512,7 +783,9 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
512
783
|
if (hasProperty(params, node.name)) {
|
|
513
784
|
const ix = params[node.name];
|
|
514
785
|
if (ix >= 0) {
|
|
515
|
-
replacement = call.arguments[ix];
|
|
786
|
+
replacement = { ...call.arguments[ix] };
|
|
787
|
+
replacements.add(replacement);
|
|
788
|
+
return replacement;
|
|
516
789
|
}
|
|
517
790
|
break;
|
|
518
791
|
}
|
|
@@ -549,6 +822,10 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
549
822
|
}
|
|
550
823
|
}
|
|
551
824
|
function inliner_unused(expression, top) {
|
|
825
|
+
const estmt = (expression) => withLoc({
|
|
826
|
+
type: "ExpressionStatement",
|
|
827
|
+
expression,
|
|
828
|
+
}, expression);
|
|
552
829
|
switch (expression.type) {
|
|
553
830
|
case "Literal":
|
|
554
831
|
return [];
|
|
@@ -560,9 +837,50 @@ function inliner_unused(expression, top) {
|
|
|
560
837
|
if (expression.operator === "as") {
|
|
561
838
|
return inliner_unused(expression.left);
|
|
562
839
|
}
|
|
563
|
-
// fall through
|
|
564
|
-
case "LogicalExpression":
|
|
565
840
|
return inliner_unused(expression.left).concat(inliner_unused(expression.right));
|
|
841
|
+
case "LogicalExpression": {
|
|
842
|
+
const right = inliner_unused(expression.right);
|
|
843
|
+
if (!right.length)
|
|
844
|
+
return inliner_unused(expression.left);
|
|
845
|
+
const consequent = withLoc({
|
|
846
|
+
type: "BlockStatement",
|
|
847
|
+
body: [estmt(expression.right)],
|
|
848
|
+
}, expression.right);
|
|
849
|
+
let alternate;
|
|
850
|
+
if (expression.operator == "||") {
|
|
851
|
+
alternate = { ...consequent };
|
|
852
|
+
consequent.body = [];
|
|
853
|
+
}
|
|
854
|
+
return [
|
|
855
|
+
withLoc({
|
|
856
|
+
type: "IfStatement",
|
|
857
|
+
test: expression.left,
|
|
858
|
+
consequent,
|
|
859
|
+
alternate,
|
|
860
|
+
}, expression),
|
|
861
|
+
];
|
|
862
|
+
}
|
|
863
|
+
case "ConditionalExpression": {
|
|
864
|
+
const consequentExprs = inliner_unused(expression.consequent);
|
|
865
|
+
const alternateExprs = inliner_unused(expression.alternate);
|
|
866
|
+
if (!consequentExprs.length && !alternateExprs.length) {
|
|
867
|
+
return inliner_unused(expression.test);
|
|
868
|
+
}
|
|
869
|
+
return [
|
|
870
|
+
withLoc({
|
|
871
|
+
type: "IfStatement",
|
|
872
|
+
test: expression.test,
|
|
873
|
+
consequent: withLoc({
|
|
874
|
+
type: "BlockStatement",
|
|
875
|
+
body: consequentExprs,
|
|
876
|
+
}, expression.consequent),
|
|
877
|
+
alternate: withLoc({
|
|
878
|
+
type: "BlockStatement",
|
|
879
|
+
body: alternateExprs,
|
|
880
|
+
}, expression.alternate),
|
|
881
|
+
}, expression),
|
|
882
|
+
];
|
|
883
|
+
}
|
|
566
884
|
case "UnaryExpression":
|
|
567
885
|
return inliner_unused(expression.argument);
|
|
568
886
|
case "MemberExpression":
|
|
@@ -577,17 +895,7 @@ function inliner_unused(expression, top) {
|
|
|
577
895
|
.map((p) => inliner_unused(p.key).concat(inliner_unused(p.value)))
|
|
578
896
|
.flat(1);
|
|
579
897
|
}
|
|
580
|
-
return top
|
|
581
|
-
? null
|
|
582
|
-
: [
|
|
583
|
-
{
|
|
584
|
-
type: "ExpressionStatement",
|
|
585
|
-
expression,
|
|
586
|
-
start: expression.start,
|
|
587
|
-
end: expression.end,
|
|
588
|
-
loc: expression.loc,
|
|
589
|
-
},
|
|
590
|
-
];
|
|
898
|
+
return top ? null : [estmt(expression)];
|
|
591
899
|
}
|
|
592
900
|
function inliner_diagnostic(state, loc, message, type = "INFO") {
|
|
593
901
|
if (!loc || !loc.source)
|
|
@@ -620,6 +928,10 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
620
928
|
if (!func.node || !func.node.body) {
|
|
621
929
|
return null;
|
|
622
930
|
}
|
|
931
|
+
const lastStmt = (block) => {
|
|
932
|
+
const last = block.body.slice(-1)[0];
|
|
933
|
+
return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
|
|
934
|
+
};
|
|
623
935
|
let retStmtCount = 0;
|
|
624
936
|
if (context.type === "ReturnStatement") {
|
|
625
937
|
const last = func.node.body.body.slice(-1)[0];
|
|
@@ -635,15 +947,19 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
635
947
|
if (retStmtCount > 1) {
|
|
636
948
|
inlineDiagnostic(state, func, call, "Function had more than one return statement");
|
|
637
949
|
}
|
|
638
|
-
else if (context.type === "AssignmentExpression"
|
|
950
|
+
else if ((context.type === "AssignmentExpression" ||
|
|
951
|
+
context.type === "VariableDeclarator") &&
|
|
952
|
+
retStmtCount !== 1) {
|
|
639
953
|
inlineDiagnostic(state, func, call, "Function did not have a return statement");
|
|
640
954
|
return null;
|
|
641
955
|
}
|
|
642
956
|
if (retStmtCount === 1) {
|
|
643
|
-
const last = func.node.body
|
|
957
|
+
const [last] = lastStmt(func.node.body);
|
|
644
958
|
if (!last ||
|
|
645
959
|
last.type !== "ReturnStatement" ||
|
|
646
|
-
(context.type === "AssignmentExpression"
|
|
960
|
+
((context.type === "AssignmentExpression" ||
|
|
961
|
+
context.type === "VariableDeclarator") &&
|
|
962
|
+
!last.argument)) {
|
|
647
963
|
inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
|
|
648
964
|
return null;
|
|
649
965
|
}
|
|
@@ -663,143 +979,1533 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
663
979
|
}
|
|
664
980
|
inliner_diagnostic(state, call.loc, null);
|
|
665
981
|
if (context.type !== "ReturnStatement" && retStmtCount) {
|
|
666
|
-
const last = body
|
|
982
|
+
const [last, block] = lastStmt(body);
|
|
667
983
|
if (last.type != "ReturnStatement") {
|
|
668
984
|
throw new Error("ReturnStatement got lost!");
|
|
669
985
|
}
|
|
670
|
-
if (
|
|
671
|
-
context.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
986
|
+
if (last.argument) {
|
|
987
|
+
if (context.type === "AssignmentExpression") {
|
|
988
|
+
context.right = last.argument;
|
|
989
|
+
block.body[block.body.length - 1] = {
|
|
990
|
+
type: "ExpressionStatement",
|
|
991
|
+
expression: context,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
else if (context.type === "VariableDeclarator") {
|
|
995
|
+
const { id, init: _init, kind: _kind, ...rest } = context;
|
|
996
|
+
block.body[block.body.length - 1] = {
|
|
997
|
+
...rest,
|
|
998
|
+
type: "ExpressionStatement",
|
|
999
|
+
expression: {
|
|
1000
|
+
...rest,
|
|
1001
|
+
type: "AssignmentExpression",
|
|
1002
|
+
operator: "=",
|
|
1003
|
+
left: id.type === "Identifier" ? id : id.left,
|
|
1004
|
+
right: last.argument,
|
|
1005
|
+
},
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
const side_exprs = inliner_unused(last.argument);
|
|
1010
|
+
block.body.splice(block.body.length - 1, 1, ...side_exprs);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
--block.body.length;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return body;
|
|
1018
|
+
}
|
|
1019
|
+
function inliner_inlineFunction(state, func, call, context) {
|
|
1020
|
+
if (context) {
|
|
1021
|
+
return inlineWithArgs(state, func, call, context);
|
|
1022
|
+
}
|
|
1023
|
+
const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
|
|
1024
|
+
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
1025
|
+
const map = fixupLocalsMap(state);
|
|
1026
|
+
const ret = processInlineBody(state, func, call, retArg, params);
|
|
1027
|
+
state.localsStack[state.localsStack.length - 1].map = map;
|
|
1028
|
+
return ret;
|
|
1029
|
+
}
|
|
1030
|
+
function applyTypeIfNeeded(node) {
|
|
1031
|
+
if ("enumType" in node && node.enumType) {
|
|
1032
|
+
node = {
|
|
1033
|
+
type: "BinaryExpression",
|
|
1034
|
+
operator: "as",
|
|
1035
|
+
left: node,
|
|
1036
|
+
right: { type: "TypeSpecList", ts: [node.enumType] },
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
return node;
|
|
1040
|
+
}
|
|
1041
|
+
function fixNodeScope(state, lookupNode, nodeStack) {
|
|
1042
|
+
if (lookupNode.type === "Identifier") {
|
|
1043
|
+
const locals = state.localsStack[state.localsStack.length - 1];
|
|
1044
|
+
const { map } = locals;
|
|
1045
|
+
if (!map)
|
|
1046
|
+
throw new Error("No local variable map!");
|
|
1047
|
+
if (hasProperty(map, lookupNode.name) && map[lookupNode.name] !== false) {
|
|
1048
|
+
// map[name] !== false means its an entry that was created during inlining
|
|
1049
|
+
// so its definitely one of our locals.
|
|
1050
|
+
return lookupNode;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
const [, original] = state.lookup(lookupNode, null, nodeStack);
|
|
1054
|
+
if (!original) {
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
const [, current] = state.lookup(lookupNode);
|
|
1058
|
+
// For now, leave it alone if it already maps to the same thing.
|
|
1059
|
+
// With a bit more work, we could find the guaranteed shortest
|
|
1060
|
+
// reference, and then use this to optimize *all* symbols, not
|
|
1061
|
+
// just fix inlined ones.
|
|
1062
|
+
if (current && sameLookupResult(original, current)) {
|
|
1063
|
+
return lookupNode;
|
|
1064
|
+
}
|
|
1065
|
+
const node = lookupNode.type === "Identifier"
|
|
1066
|
+
? lookupNode
|
|
1067
|
+
: lookupNode.property;
|
|
1068
|
+
if (original.length === 1 &&
|
|
1069
|
+
original[0].results.length === 1 &&
|
|
1070
|
+
original[0].results[0].type === "EnumStringMember") {
|
|
1071
|
+
return applyTypeIfNeeded(original[0].results[0].init);
|
|
1072
|
+
}
|
|
1073
|
+
const prefixes = original
|
|
1074
|
+
.map((lookupDef) => lookupDef.results.map((sn) => {
|
|
1075
|
+
if (isStateNode(sn) && sn.fullName) {
|
|
1076
|
+
return sn.fullName;
|
|
1077
|
+
}
|
|
1078
|
+
return "";
|
|
1079
|
+
}))
|
|
1080
|
+
.flat();
|
|
1081
|
+
const member = (object, property) => ({
|
|
1082
|
+
type: "MemberExpression",
|
|
1083
|
+
object,
|
|
1084
|
+
property,
|
|
1085
|
+
computed: false,
|
|
1086
|
+
start: node.start,
|
|
1087
|
+
end: node.end,
|
|
1088
|
+
loc: node.loc,
|
|
1089
|
+
});
|
|
1090
|
+
if (prefixes.length &&
|
|
1091
|
+
prefixes[0].startsWith("$.") &&
|
|
1092
|
+
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
1093
|
+
const prefix = prefixes[0].split(".").slice(0, -1).reverse();
|
|
1094
|
+
let found = false;
|
|
1095
|
+
return prefix.reduce((current, name) => {
|
|
1096
|
+
if (found)
|
|
1097
|
+
return current;
|
|
1098
|
+
const [, results] = state.lookup(current);
|
|
1099
|
+
if (results && sameLookupResult(original, results)) {
|
|
1100
|
+
found = true;
|
|
1101
|
+
return current;
|
|
1102
|
+
}
|
|
1103
|
+
const object = {
|
|
1104
|
+
type: "Identifier",
|
|
1105
|
+
name,
|
|
1106
|
+
start: node.start,
|
|
1107
|
+
end: node.end,
|
|
1108
|
+
loc: node.loc,
|
|
1109
|
+
};
|
|
1110
|
+
let root = null;
|
|
1111
|
+
let property = current;
|
|
1112
|
+
do {
|
|
1113
|
+
root = property;
|
|
1114
|
+
property = property.object;
|
|
1115
|
+
} while (property.type === "MemberExpression");
|
|
1116
|
+
if (property.type === "ThisExpression") {
|
|
1117
|
+
root.object = object;
|
|
1118
|
+
return root;
|
|
1119
|
+
}
|
|
1120
|
+
root.object = member(object, property);
|
|
1121
|
+
return current;
|
|
1122
|
+
}, member({
|
|
1123
|
+
type: "ThisExpression",
|
|
1124
|
+
text: "self",
|
|
1125
|
+
start: node.start,
|
|
1126
|
+
end: node.end,
|
|
1127
|
+
loc: node.loc,
|
|
1128
|
+
}, node));
|
|
1129
|
+
}
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
;// CONCATENATED MODULE: external "./util.cjs"
|
|
1134
|
+
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
1135
|
+
;// CONCATENATED MODULE: ./src/control-flow.ts
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
const Terminals = {
|
|
1139
|
+
BreakStatement: "break",
|
|
1140
|
+
ContinueStatement: "continue",
|
|
1141
|
+
ReturnStatement: null,
|
|
1142
|
+
ThrowStatement: "throw",
|
|
1143
|
+
};
|
|
1144
|
+
class LocalState {
|
|
1145
|
+
constructor(func) {
|
|
1146
|
+
this.stack = [];
|
|
1147
|
+
this.info = new Map();
|
|
1148
|
+
this.curBlock = {};
|
|
1149
|
+
this.unreachable = false;
|
|
1150
|
+
this.push(func);
|
|
1151
|
+
}
|
|
1152
|
+
push(node) {
|
|
1153
|
+
const top = { node };
|
|
1154
|
+
this.stack.push(top);
|
|
1155
|
+
return top;
|
|
1156
|
+
}
|
|
1157
|
+
pop() {
|
|
1158
|
+
return this.stack.pop();
|
|
1159
|
+
}
|
|
1160
|
+
top(depth) {
|
|
1161
|
+
return this.stack[this.stack.length - (depth || 1)];
|
|
1162
|
+
}
|
|
1163
|
+
addEdge(from, to) {
|
|
1164
|
+
if (!from.succs) {
|
|
1165
|
+
from.succs = [to];
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
pushUnique(from.succs, to);
|
|
1169
|
+
}
|
|
1170
|
+
if (!to.preds) {
|
|
1171
|
+
to.preds = [from];
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
pushUnique(to.preds, from);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
newBlock(block) {
|
|
1178
|
+
if (!block)
|
|
1179
|
+
block = {};
|
|
1180
|
+
if (!this.unreachable) {
|
|
1181
|
+
this.addEdge(this.curBlock, block);
|
|
1182
|
+
}
|
|
1183
|
+
this.unreachable = false;
|
|
1184
|
+
for (let i = this.stack.length; i--;) {
|
|
1185
|
+
const si = this.stack[i];
|
|
1186
|
+
if (si.throw) {
|
|
1187
|
+
block.exsucc = si.throw;
|
|
1188
|
+
if (!si.throw.expreds) {
|
|
1189
|
+
si.throw.expreds = [block];
|
|
1190
|
+
}
|
|
1191
|
+
else {
|
|
1192
|
+
si.throw.expreds.push(block);
|
|
1193
|
+
}
|
|
1194
|
+
break;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return (this.curBlock = block);
|
|
1198
|
+
}
|
|
1199
|
+
terminal(type) {
|
|
1200
|
+
const re = Terminals[type];
|
|
1201
|
+
if (re) {
|
|
1202
|
+
for (let i = this.stack.length; i--;) {
|
|
1203
|
+
const target = this.stack[i][re];
|
|
1204
|
+
if (target) {
|
|
1205
|
+
this.addEdge(this.curBlock, target);
|
|
1206
|
+
break;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
this.unreachable = true;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function control_flow_buildReducedGraph(state, func, notice) {
|
|
1214
|
+
const { stack, pre, post } = state;
|
|
1215
|
+
try {
|
|
1216
|
+
const localState = new LocalState(func.node);
|
|
1217
|
+
const ret = localState.curBlock;
|
|
1218
|
+
state.stack = func.stack;
|
|
1219
|
+
const stmtStack = [func.node];
|
|
1220
|
+
let tryActive = 0;
|
|
1221
|
+
state.pre = (node) => {
|
|
1222
|
+
if (state.inType || localState.unreachable) {
|
|
1223
|
+
return [];
|
|
1224
|
+
}
|
|
1225
|
+
if (!localState.curBlock.node &&
|
|
1226
|
+
(isStatement(node) || isExpression(node))) {
|
|
1227
|
+
localState.curBlock.node = node;
|
|
1228
|
+
}
|
|
1229
|
+
if (isStatement(node)) {
|
|
1230
|
+
stmtStack.push(node);
|
|
1231
|
+
}
|
|
1232
|
+
switch (node.type) {
|
|
1233
|
+
case "AttributeList":
|
|
1234
|
+
return [];
|
|
1235
|
+
case "SwitchStatement": {
|
|
1236
|
+
const top = localState.push(node);
|
|
1237
|
+
top.break = {};
|
|
1238
|
+
state.traverse(node.discriminant);
|
|
1239
|
+
const testBlocks = [];
|
|
1240
|
+
let defaultSeen = false;
|
|
1241
|
+
node.cases.forEach((sc, i) => {
|
|
1242
|
+
if (sc.test) {
|
|
1243
|
+
state.traverse(sc.test);
|
|
1244
|
+
testBlocks[i] = localState.curBlock;
|
|
1245
|
+
localState.newBlock();
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
defaultSeen = true;
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
const endOfTests = localState.curBlock;
|
|
1252
|
+
if (!defaultSeen) {
|
|
1253
|
+
localState.addEdge(endOfTests, top.break);
|
|
1254
|
+
}
|
|
1255
|
+
localState.unreachable = true;
|
|
1256
|
+
node.cases.forEach((sc, i) => {
|
|
1257
|
+
localState.newBlock();
|
|
1258
|
+
localState.addEdge(testBlocks[i] || endOfTests, localState.curBlock);
|
|
1259
|
+
sc.consequent.every((s) => {
|
|
1260
|
+
state.traverse(s);
|
|
1261
|
+
return !localState.unreachable;
|
|
1262
|
+
});
|
|
1263
|
+
});
|
|
1264
|
+
localState.newBlock(top.break);
|
|
1265
|
+
localState.unreachable = !top.break.preds;
|
|
1266
|
+
return [];
|
|
1267
|
+
}
|
|
1268
|
+
case "DoWhileStatement":
|
|
1269
|
+
case "WhileStatement": {
|
|
1270
|
+
localState.push(node);
|
|
1271
|
+
const top = localState.top();
|
|
1272
|
+
top.break = {};
|
|
1273
|
+
top.continue = {};
|
|
1274
|
+
let head;
|
|
1275
|
+
if (node.type === "WhileStatement") {
|
|
1276
|
+
head = localState.newBlock(top.continue);
|
|
1277
|
+
state.traverse(node.test);
|
|
1278
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
1279
|
+
localState.newBlock();
|
|
1280
|
+
}
|
|
1281
|
+
else {
|
|
1282
|
+
head = localState.newBlock();
|
|
1283
|
+
}
|
|
1284
|
+
state.traverse(node.body);
|
|
1285
|
+
if (node.type === "DoWhileStatement") {
|
|
1286
|
+
localState.newBlock(top.continue);
|
|
1287
|
+
state.traverse(node.test);
|
|
1288
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
1289
|
+
}
|
|
1290
|
+
localState.addEdge(localState.curBlock, head);
|
|
1291
|
+
localState.curBlock = top.break;
|
|
1292
|
+
return [];
|
|
1293
|
+
}
|
|
1294
|
+
case "TryStatement": {
|
|
1295
|
+
const top = localState.push(node);
|
|
1296
|
+
const catches = (top.throw = {});
|
|
1297
|
+
// This edge shouldn't exist, but we can trigger
|
|
1298
|
+
// (incorrect) "variable may not be initialized" errors
|
|
1299
|
+
// in the monkey c compiler without it.
|
|
1300
|
+
// https://forums.garmin.com/developer/connect-iq/i/bug-reports/incorrect-maybe-uninitialized-error
|
|
1301
|
+
localState.addEdge(localState.curBlock, top.throw);
|
|
1302
|
+
localState.newBlock();
|
|
1303
|
+
tryActive++;
|
|
1304
|
+
state.traverse(node.block);
|
|
1305
|
+
tryActive--;
|
|
1306
|
+
delete top.throw;
|
|
1307
|
+
top.posttry = {};
|
|
1308
|
+
const tryFallsThrough = !localState.unreachable;
|
|
1309
|
+
if (node.finalizer) {
|
|
1310
|
+
tryActive++;
|
|
1311
|
+
top.throw = top.finally = {};
|
|
1312
|
+
// curBlock branches to finally, no matter how it exits.
|
|
1313
|
+
localState.addEdge(localState.curBlock, top.finally);
|
|
1314
|
+
}
|
|
1315
|
+
else {
|
|
1316
|
+
if (!localState.unreachable) {
|
|
1317
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
localState.unreachable = true;
|
|
1321
|
+
localState.newBlock(catches);
|
|
1322
|
+
if (node.handler) {
|
|
1323
|
+
state.traverse(node.handler);
|
|
1324
|
+
if (top.throw) {
|
|
1325
|
+
tryActive--;
|
|
1326
|
+
delete top.throw;
|
|
1327
|
+
}
|
|
1328
|
+
// Each "catch (ex instanceof Foo)" chains to the next,
|
|
1329
|
+
// but "catch (ex)" terminates the list. If the end
|
|
1330
|
+
// of the chain has a predecessor, its possible that
|
|
1331
|
+
// none of the conditions matched, so the exception
|
|
1332
|
+
// will propagate from there.
|
|
1333
|
+
if (localState.curBlock.preds) {
|
|
1334
|
+
localState.terminal("ThrowStatement");
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
if (top.throw) {
|
|
1338
|
+
tryActive--;
|
|
1339
|
+
delete top.throw;
|
|
1340
|
+
}
|
|
1341
|
+
if (node.finalizer) {
|
|
1342
|
+
localState.unreachable = true;
|
|
1343
|
+
localState.newBlock(top.finally);
|
|
1344
|
+
delete top.finally;
|
|
1345
|
+
state.traverse(node.finalizer);
|
|
1346
|
+
if (tryFallsThrough && !localState.unreachable) {
|
|
1347
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
1348
|
+
}
|
|
1349
|
+
localState.terminal("ThrowStatement");
|
|
1350
|
+
}
|
|
1351
|
+
localState.unreachable = true;
|
|
1352
|
+
localState.newBlock(top.posttry);
|
|
1353
|
+
return [];
|
|
1354
|
+
}
|
|
1355
|
+
case "CatchClause": {
|
|
1356
|
+
const top = localState.top();
|
|
1357
|
+
if (!localState.curBlock.preds && !localState.curBlock.expreds) {
|
|
1358
|
+
return [];
|
|
1359
|
+
}
|
|
1360
|
+
const next = {};
|
|
1361
|
+
if (node.param && node.param.type === "BinaryExpression") {
|
|
1362
|
+
state.traverse(node.param);
|
|
1363
|
+
localState.addEdge(localState.curBlock, next);
|
|
1364
|
+
localState.newBlock();
|
|
1365
|
+
}
|
|
1366
|
+
state.traverse(node.body);
|
|
1367
|
+
if (top.finally) {
|
|
1368
|
+
// this edge exists even if this point is unreachable
|
|
1369
|
+
localState.addEdge(localState.curBlock, top.finally);
|
|
1370
|
+
}
|
|
1371
|
+
if (!localState.unreachable) {
|
|
1372
|
+
if (!top.posttry)
|
|
1373
|
+
top.posttry = {};
|
|
1374
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
1375
|
+
}
|
|
1376
|
+
localState.unreachable = true;
|
|
1377
|
+
localState.newBlock(next);
|
|
1378
|
+
return [];
|
|
1379
|
+
}
|
|
1380
|
+
case "ForStatement": {
|
|
1381
|
+
const top = localState.push(node);
|
|
1382
|
+
if (node.init)
|
|
1383
|
+
state.traverse(node.init);
|
|
1384
|
+
const head = localState.newBlock();
|
|
1385
|
+
top.break = {};
|
|
1386
|
+
top.continue = {};
|
|
1387
|
+
if (node.test) {
|
|
1388
|
+
state.traverse(node.test);
|
|
1389
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
1390
|
+
localState.newBlock();
|
|
1391
|
+
}
|
|
1392
|
+
state.traverse(node.body);
|
|
1393
|
+
localState.newBlock(top.continue);
|
|
1394
|
+
if (node.update) {
|
|
1395
|
+
state.traverse(node.update);
|
|
1396
|
+
}
|
|
1397
|
+
if (!localState.unreachable) {
|
|
1398
|
+
localState.addEdge(localState.curBlock, head);
|
|
1399
|
+
}
|
|
1400
|
+
// there is no fall through from the end of the loop
|
|
1401
|
+
// to the next block. The only way there is via break
|
|
1402
|
+
// or the test failing.
|
|
1403
|
+
localState.unreachable = true;
|
|
1404
|
+
localState.newBlock(top.break);
|
|
1405
|
+
if (!top.break.preds) {
|
|
1406
|
+
localState.unreachable = true;
|
|
1407
|
+
}
|
|
1408
|
+
return [];
|
|
1409
|
+
}
|
|
1410
|
+
case "IfStatement":
|
|
1411
|
+
case "ConditionalExpression": {
|
|
1412
|
+
state.traverse(node.test);
|
|
1413
|
+
const alternate = {};
|
|
1414
|
+
localState.addEdge(localState.curBlock, alternate);
|
|
1415
|
+
localState.newBlock();
|
|
1416
|
+
state.traverse(node.consequent);
|
|
1417
|
+
const consequent = localState.unreachable
|
|
1418
|
+
? null
|
|
1419
|
+
: localState.curBlock;
|
|
1420
|
+
localState.unreachable = true;
|
|
1421
|
+
localState.newBlock(alternate);
|
|
1422
|
+
if (node.alternate) {
|
|
1423
|
+
state.traverse(node.alternate);
|
|
1424
|
+
if (!localState.unreachable) {
|
|
1425
|
+
localState.newBlock();
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
if (consequent) {
|
|
1429
|
+
if (localState.unreachable) {
|
|
1430
|
+
localState.newBlock();
|
|
1431
|
+
}
|
|
1432
|
+
localState.addEdge(consequent, localState.curBlock);
|
|
1433
|
+
}
|
|
1434
|
+
return [];
|
|
1435
|
+
}
|
|
1436
|
+
case "LogicalExpression": {
|
|
1437
|
+
state.traverse(node.left);
|
|
1438
|
+
if (localState.unreachable)
|
|
1439
|
+
break;
|
|
1440
|
+
const mid = localState.curBlock;
|
|
1441
|
+
localState.newBlock();
|
|
1442
|
+
state.traverse(node.right);
|
|
1443
|
+
localState.newBlock();
|
|
1444
|
+
localState.addEdge(mid, localState.curBlock);
|
|
1445
|
+
return [];
|
|
1446
|
+
}
|
|
1447
|
+
case "VariableDeclarator":
|
|
1448
|
+
return ["init"];
|
|
1449
|
+
case "MemberExpression":
|
|
1450
|
+
if (!node.computed) {
|
|
1451
|
+
return ["object"];
|
|
1452
|
+
}
|
|
1453
|
+
break;
|
|
1454
|
+
case "UnaryExpression":
|
|
1455
|
+
if (node.operator === ":") {
|
|
1456
|
+
return [];
|
|
1457
|
+
}
|
|
1458
|
+
break;
|
|
1459
|
+
case "UpdateExpression":
|
|
1460
|
+
// We don't want to traverse the argument, since then it would
|
|
1461
|
+
// look like a ref, rather than a def. But if its a
|
|
1462
|
+
// MemberExpression, we *do* want to traverse the subexpressions
|
|
1463
|
+
// as potential refs.
|
|
1464
|
+
if (node.argument.type === "MemberExpression") {
|
|
1465
|
+
state.traverse(node.argument.object);
|
|
1466
|
+
if (node.argument.computed) {
|
|
1467
|
+
state.traverse(node.argument.property);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return [];
|
|
1471
|
+
case "AssignmentExpression":
|
|
1472
|
+
if (node.left.type === "MemberExpression") {
|
|
1473
|
+
state.traverse(node.left.object);
|
|
1474
|
+
if (node.left.computed) {
|
|
1475
|
+
state.traverse(node.left.property);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
return ["right"];
|
|
1479
|
+
case "ThrowStatement":
|
|
1480
|
+
case "ReturnStatement":
|
|
1481
|
+
if (node.argument) {
|
|
1482
|
+
state.traverse(node.argument);
|
|
1483
|
+
}
|
|
1484
|
+
// fall through
|
|
1485
|
+
case "BreakStatement":
|
|
1486
|
+
case "ContinueStatement":
|
|
1487
|
+
localState.terminal(node.type);
|
|
1488
|
+
return [];
|
|
1489
|
+
}
|
|
1490
|
+
return null;
|
|
1491
|
+
};
|
|
1492
|
+
const addEvent = (block, event) => {
|
|
1493
|
+
if (!block.events) {
|
|
1494
|
+
block.events = [event];
|
|
1495
|
+
}
|
|
1496
|
+
else {
|
|
1497
|
+
block.events.push(event);
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
state.post = (node) => {
|
|
1501
|
+
const curStmt = stmtStack[stmtStack.length - 1];
|
|
1502
|
+
if (!state.inType) {
|
|
1503
|
+
const throws = tryActive > 0 && mayThrow(node);
|
|
1504
|
+
const event = notice(node, curStmt, throws);
|
|
1505
|
+
if (throws) {
|
|
1506
|
+
if (!event) {
|
|
1507
|
+
throw new Error("mayThrow expression in try/catch must generate an event");
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
else if (event) {
|
|
1511
|
+
event.mayThrow = false;
|
|
1512
|
+
}
|
|
1513
|
+
if (event) {
|
|
1514
|
+
if (event.mayThrow) {
|
|
1515
|
+
for (let i = localState.stack.length; i--;) {
|
|
1516
|
+
const target = localState.stack[i].throw;
|
|
1517
|
+
if (target) {
|
|
1518
|
+
if (localState.curBlock.exsucc) {
|
|
1519
|
+
if (localState.curBlock.exsucc !== target) {
|
|
1520
|
+
throw new Error(`Block has multiple throw targets`);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
else {
|
|
1524
|
+
localState.curBlock.exsucc = target;
|
|
1525
|
+
if (!target.expreds) {
|
|
1526
|
+
target.expreds = [localState.curBlock];
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
target.expreds.push(localState.curBlock);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
break;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
addEvent(localState.curBlock, event);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
if (curStmt === node) {
|
|
1540
|
+
stmtStack.pop();
|
|
1541
|
+
}
|
|
1542
|
+
if (localState.top().node === node) {
|
|
1543
|
+
localState.pop();
|
|
1544
|
+
}
|
|
1545
|
+
return null;
|
|
1546
|
+
};
|
|
1547
|
+
state.traverse(func.node);
|
|
1548
|
+
return cleanCfg(ret);
|
|
1549
|
+
}
|
|
1550
|
+
finally {
|
|
1551
|
+
state.pre = pre;
|
|
1552
|
+
state.post = post;
|
|
1553
|
+
state.stack = stack;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
function cleanCfg(head) {
|
|
1557
|
+
preOrderTraverse(head, (cur) => {
|
|
1558
|
+
if (cur.succs && cur.succs.length === 1) {
|
|
1559
|
+
const succ = cur.succs[0];
|
|
1560
|
+
if (succ !== head &&
|
|
1561
|
+
succ.preds.length === 1 &&
|
|
1562
|
+
(!cur.exsucc || cur.exsucc === succ.exsucc) &&
|
|
1563
|
+
(!succ.succs ||
|
|
1564
|
+
succ.succs.length === 1 ||
|
|
1565
|
+
(cur.preds && cur.preds.length === 1))) {
|
|
1566
|
+
if (cur.events) {
|
|
1567
|
+
if (succ.events) {
|
|
1568
|
+
cur.events.push(...succ.events);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
else if (succ.events) {
|
|
1572
|
+
cur.events = succ.events;
|
|
1573
|
+
}
|
|
1574
|
+
if (succ.exsucc) {
|
|
1575
|
+
const preds = succ.exsucc.expreds;
|
|
1576
|
+
for (let i = preds.length; i--;) {
|
|
1577
|
+
if (preds[i] === succ) {
|
|
1578
|
+
// If cur has an exsucc, we already
|
|
1579
|
+
// checked that its the same as succ's,
|
|
1580
|
+
// so we can just delete the edge.
|
|
1581
|
+
// Otherwise, we need to point it at cur.
|
|
1582
|
+
if (cur.exsucc) {
|
|
1583
|
+
preds.splice(i, 1);
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
preds[i] = cur;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
cur.exsucc = succ.exsucc;
|
|
1592
|
+
cur.succs = succ.succs;
|
|
1593
|
+
if (cur.succs) {
|
|
1594
|
+
cur.succs.forEach((s) => s.preds.forEach((p, i, arr) => {
|
|
1595
|
+
if (p === succ) {
|
|
1596
|
+
arr[i] = cur;
|
|
1597
|
+
}
|
|
1598
|
+
}));
|
|
1599
|
+
}
|
|
1600
|
+
if (!cur.node)
|
|
1601
|
+
cur.node = succ.node;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
});
|
|
1605
|
+
return head;
|
|
1606
|
+
}
|
|
1607
|
+
function postOrderTraverse(head, visitor) {
|
|
1608
|
+
const visited = new Set();
|
|
1609
|
+
const helper = (cur) => {
|
|
1610
|
+
if (visited.has(cur))
|
|
1611
|
+
return;
|
|
1612
|
+
visited.add(cur);
|
|
1613
|
+
if (cur.succs) {
|
|
1614
|
+
cur.succs.forEach((block) => helper(block));
|
|
1615
|
+
}
|
|
1616
|
+
if (cur.exsucc)
|
|
1617
|
+
helper(cur.exsucc);
|
|
1618
|
+
visitor(cur);
|
|
1619
|
+
};
|
|
1620
|
+
helper(head);
|
|
1621
|
+
}
|
|
1622
|
+
function preOrderTraverse(head, visitor) {
|
|
1623
|
+
const visited = new Set();
|
|
1624
|
+
const helper = (cur) => {
|
|
1625
|
+
if (visited.has(cur))
|
|
1626
|
+
return;
|
|
1627
|
+
visited.add(cur);
|
|
1628
|
+
visitor(cur);
|
|
1629
|
+
if (cur.succs) {
|
|
1630
|
+
cur.succs.forEach((block) => helper(block));
|
|
1631
|
+
}
|
|
1632
|
+
if (cur.exsucc)
|
|
1633
|
+
helper(cur.exsucc);
|
|
1634
|
+
};
|
|
1635
|
+
helper(head);
|
|
1636
|
+
}
|
|
1637
|
+
function control_flow_getPostOrder(head) {
|
|
1638
|
+
const blocks = [];
|
|
1639
|
+
postOrderTraverse(head, (block) => blocks.push(block));
|
|
1640
|
+
return blocks;
|
|
1641
|
+
}
|
|
1642
|
+
function getPreOrder(head) {
|
|
1643
|
+
const blocks = [];
|
|
1644
|
+
postOrderTraverse(head, (block) => blocks.push(block));
|
|
1645
|
+
return blocks;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
// EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
|
|
1649
|
+
var priorityqueuejs = __webpack_require__(2789);
|
|
1650
|
+
;// CONCATENATED MODULE: ./src/pre.ts
|
|
1651
|
+
|
|
1652
|
+
|
|
1653
|
+
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* This implements a pseudo Partial Redundancy Elimination
|
|
1657
|
+
* pass. It isn't quite like traditional PRE because we're
|
|
1658
|
+
* aiming to minimize size, not dynamic instructions. So
|
|
1659
|
+
* for us, its worthwhile to take something like:
|
|
1660
|
+
*
|
|
1661
|
+
* switch (x) {
|
|
1662
|
+
* case 1: foo(A.B); break;
|
|
1663
|
+
* case 2: foo(C); break;
|
|
1664
|
+
* case 3: bar(A.B); break;
|
|
1665
|
+
* }
|
|
1666
|
+
*
|
|
1667
|
+
* and rewrite it as
|
|
1668
|
+
*
|
|
1669
|
+
* var tmp = A.B;
|
|
1670
|
+
* switch (x) {
|
|
1671
|
+
* case 1: foo(tmp); break;
|
|
1672
|
+
* case 2: foo(C); break;
|
|
1673
|
+
* case 3: bar(tmp); break;
|
|
1674
|
+
* }
|
|
1675
|
+
*
|
|
1676
|
+
* because even though A.B wasn't used on all paths where we
|
|
1677
|
+
* inserted the temporary, we still reduced the code size.
|
|
1678
|
+
*/
|
|
1679
|
+
const logging = false;
|
|
1680
|
+
function declFullName(decl) {
|
|
1681
|
+
switch (decl.type) {
|
|
1682
|
+
case "Literal":
|
|
1683
|
+
return decl.raw || decl.value?.toString() || "null";
|
|
1684
|
+
case "VariableDeclarator":
|
|
1685
|
+
return decl.fullName;
|
|
1686
|
+
default:
|
|
1687
|
+
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
function declName(decl) {
|
|
1691
|
+
switch (decl.type) {
|
|
1692
|
+
case "Literal":
|
|
1693
|
+
return (decl.raw || decl.value?.toString() || "null").replace(/[^\w]/g, "_");
|
|
1694
|
+
case "VariableDeclarator":
|
|
1695
|
+
return decl.name;
|
|
1696
|
+
default:
|
|
1697
|
+
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
function logAntState(s, decl) {
|
|
1701
|
+
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
1702
|
+
if (event.type === "def" || event.type === "mod")
|
|
1703
|
+
defs++;
|
|
1704
|
+
return defs;
|
|
1705
|
+
}, 0);
|
|
1706
|
+
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
|
|
1707
|
+
console.log(` - members: ${Array.from(s.members)
|
|
1708
|
+
.map(([block, live]) => block.order + (live ? "t" : "f"))
|
|
1709
|
+
.join(", ")}`);
|
|
1710
|
+
}
|
|
1711
|
+
function logAntDecls(antDecls) {
|
|
1712
|
+
antDecls.forEach(logAntState);
|
|
1713
|
+
}
|
|
1714
|
+
function pre_sizeBasedPRE(state, func) {
|
|
1715
|
+
if (!func.node.body)
|
|
1716
|
+
return;
|
|
1717
|
+
if (!state.config ||
|
|
1718
|
+
!state.config.sizeBasedPRE ||
|
|
1719
|
+
(typeof state.config.sizeBasedPRE === "string" &&
|
|
1720
|
+
state.config.sizeBasedPRE !== func.fullName)) {
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
1724
|
+
const candidates = computeAttributes(head);
|
|
1725
|
+
if (candidates) {
|
|
1726
|
+
if (logging) {
|
|
1727
|
+
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
1728
|
+
logAntDecls(candidates);
|
|
1729
|
+
}
|
|
1730
|
+
const nodeMap = new Map();
|
|
1731
|
+
const declMap = new Map();
|
|
1732
|
+
const variableDecl = withLoc({
|
|
1733
|
+
type: "VariableDeclaration",
|
|
1734
|
+
declarations: [],
|
|
1735
|
+
kind: "var",
|
|
1736
|
+
}, func.node.body);
|
|
1737
|
+
variableDecl.end = variableDecl.start;
|
|
1738
|
+
variableDecl.loc.end = variableDecl.loc.start;
|
|
1739
|
+
candidates.forEach((s, decl) => {
|
|
1740
|
+
let name;
|
|
1741
|
+
let i = 0;
|
|
1742
|
+
do {
|
|
1743
|
+
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
1744
|
+
if (!identifiers.has(name))
|
|
1745
|
+
break;
|
|
1746
|
+
i++;
|
|
1747
|
+
} while (true);
|
|
1748
|
+
declMap.set(decl, name);
|
|
1749
|
+
variableDecl.declarations.push(withLoc({
|
|
1750
|
+
type: "VariableDeclarator",
|
|
1751
|
+
id: withLoc({ type: "Identifier", name }, variableDecl),
|
|
1752
|
+
kind: "var",
|
|
1753
|
+
}, variableDecl));
|
|
1754
|
+
s.ant.forEach((event) => {
|
|
1755
|
+
const events = nodeMap.get(event.node);
|
|
1756
|
+
if (!events) {
|
|
1757
|
+
nodeMap.set(event.node, [event]);
|
|
1758
|
+
}
|
|
1759
|
+
else {
|
|
1760
|
+
events.push(event);
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
});
|
|
1764
|
+
applyReplacements(func.node, nodeMap, declMap);
|
|
1765
|
+
func.node.body.body.unshift(variableDecl);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
function unhandledExpression(node) {
|
|
1769
|
+
throw new Error(`Unhandled expression type: ${node.type}`);
|
|
1770
|
+
}
|
|
1771
|
+
function buildPREGraph(state, func) {
|
|
1772
|
+
const findDecl = (node) => {
|
|
1773
|
+
if (node.type === "Identifier" ||
|
|
1774
|
+
(node.type === "MemberExpression" && !node.computed)) {
|
|
1775
|
+
const [, results] = state.lookup(node);
|
|
1776
|
+
if (results &&
|
|
1777
|
+
results.length === 1 &&
|
|
1778
|
+
results[0].parent?.type != "BlockStatement" &&
|
|
1779
|
+
results[0].results.length === 1 &&
|
|
1780
|
+
results[0].results[0].type === "VariableDeclarator") {
|
|
1781
|
+
return results[0].results[0];
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
return null;
|
|
1785
|
+
};
|
|
1786
|
+
const literals = new Map();
|
|
1787
|
+
const identifiers = new Set();
|
|
1788
|
+
const liveDefs = new Map();
|
|
1789
|
+
const liveStmts = new Map();
|
|
1790
|
+
const liveDef = (def, stmt) => {
|
|
1791
|
+
let curNodes = liveDefs.get(def);
|
|
1792
|
+
if (!curNodes) {
|
|
1793
|
+
liveDefs.set(def, (curNodes = new Set()));
|
|
1794
|
+
}
|
|
1795
|
+
curNodes.add(stmt);
|
|
1796
|
+
let defs = liveStmts.get(stmt);
|
|
1797
|
+
if (!defs) {
|
|
1798
|
+
liveStmts.set(stmt, (defs = new Map()));
|
|
1799
|
+
}
|
|
1800
|
+
defs.set(def, (defs.get(def) || 0) + 1);
|
|
1801
|
+
};
|
|
1802
|
+
return {
|
|
1803
|
+
identifiers,
|
|
1804
|
+
graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
|
|
1805
|
+
const defs = liveStmts.get(node);
|
|
1806
|
+
if (defs) {
|
|
1807
|
+
liveStmts.delete(node);
|
|
1808
|
+
defs.forEach((count, def) => {
|
|
1809
|
+
if (count > 1) {
|
|
1810
|
+
defs.set(def, count--);
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
const v = liveDefs.get(def);
|
|
1814
|
+
if (!v || !v.has(node)) {
|
|
1815
|
+
throw new Error(`No stmt in liveDef for ${def ? declFullName(def) : "null"}`);
|
|
1816
|
+
}
|
|
1817
|
+
v.delete(node);
|
|
1818
|
+
if (!v.size) {
|
|
1819
|
+
liveDefs.delete(def);
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
switch (node.type) {
|
|
1824
|
+
case "BinaryExpression":
|
|
1825
|
+
case "UnaryExpression":
|
|
1826
|
+
case "SizedArrayExpression":
|
|
1827
|
+
case "ArrayExpression":
|
|
1828
|
+
case "ObjectExpression":
|
|
1829
|
+
case "ThisExpression":
|
|
1830
|
+
case "LogicalExpression":
|
|
1831
|
+
case "ConditionalExpression":
|
|
1832
|
+
case "SequenceExpression":
|
|
1833
|
+
case "ParenthesizedExpression":
|
|
1834
|
+
break;
|
|
1835
|
+
case "Literal":
|
|
1836
|
+
if (!node.value && refCost(node) > LocalRefCost) {
|
|
1837
|
+
let decl = literals.get(node.value);
|
|
1838
|
+
if (!decl) {
|
|
1839
|
+
decl = node;
|
|
1840
|
+
literals.set(node.value, decl);
|
|
1841
|
+
}
|
|
1842
|
+
return {
|
|
1843
|
+
type: "ref",
|
|
1844
|
+
node,
|
|
1845
|
+
decl: decl,
|
|
1846
|
+
mayThrow,
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
break;
|
|
1850
|
+
case "Identifier":
|
|
1851
|
+
identifiers.add(node.name);
|
|
1852
|
+
// fall through
|
|
1853
|
+
case "MemberExpression":
|
|
1854
|
+
{
|
|
1855
|
+
const decl = findDecl(node);
|
|
1856
|
+
if (decl && decl.type === "VariableDeclarator") {
|
|
1857
|
+
const defStmts = (decl.node.kind === "var" && liveDefs.get(null)) ||
|
|
1858
|
+
liveDefs.get(decl);
|
|
1859
|
+
if (defStmts) {
|
|
1860
|
+
break;
|
|
1861
|
+
/*
|
|
1862
|
+
// hold off on this for now. we need to communicate
|
|
1863
|
+
// which defs need to be fixed, which involves yet-another
|
|
1864
|
+
// table.
|
|
1865
|
+
|
|
1866
|
+
if (defStmts.size !== 1) break;
|
|
1867
|
+
const fixable = isFixableStmt([...defStmts][0]);
|
|
1868
|
+
if (fixable === false) break;
|
|
1869
|
+
cost += fixable;
|
|
1870
|
+
*/
|
|
1871
|
+
}
|
|
1872
|
+
return {
|
|
1873
|
+
type: "ref",
|
|
1874
|
+
node,
|
|
1875
|
+
decl,
|
|
1876
|
+
mayThrow,
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
break;
|
|
1881
|
+
case "VariableDeclarator": {
|
|
1882
|
+
const decl = findDecl(node.id.type === "BinaryExpression" ? node.id.left : node.id);
|
|
1883
|
+
if (decl) {
|
|
1884
|
+
liveDef(decl, stmt);
|
|
1885
|
+
return {
|
|
1886
|
+
type: "def",
|
|
1887
|
+
node,
|
|
1888
|
+
decl,
|
|
1889
|
+
mayThrow,
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
break;
|
|
1893
|
+
}
|
|
1894
|
+
case "AssignmentExpression": {
|
|
1895
|
+
const decl = findDecl(node.left);
|
|
1896
|
+
if (decl) {
|
|
1897
|
+
liveDef(decl, stmt);
|
|
1898
|
+
return {
|
|
1899
|
+
type: "def",
|
|
1900
|
+
node,
|
|
1901
|
+
decl,
|
|
1902
|
+
mayThrow,
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
case "UpdateExpression": {
|
|
1908
|
+
const decl = findDecl(node.argument);
|
|
1909
|
+
if (decl) {
|
|
1910
|
+
liveDef(decl, stmt);
|
|
1911
|
+
return {
|
|
1912
|
+
type: "def",
|
|
1913
|
+
node,
|
|
1914
|
+
decl,
|
|
1915
|
+
mayThrow,
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
break;
|
|
1919
|
+
}
|
|
1920
|
+
case "NewExpression":
|
|
1921
|
+
case "CallExpression":
|
|
1922
|
+
liveDef(null, stmt);
|
|
1923
|
+
return { type: "mod", node, mayThrow };
|
|
1924
|
+
default:
|
|
1925
|
+
if (!isExpression(node))
|
|
1926
|
+
break;
|
|
1927
|
+
unhandledExpression(node);
|
|
1928
|
+
}
|
|
1929
|
+
if (mayThrow) {
|
|
1930
|
+
return { type: "exn", node, mayThrow };
|
|
1931
|
+
}
|
|
1932
|
+
return null;
|
|
1933
|
+
}),
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
function anticipatedDecls() {
|
|
1937
|
+
return new Map();
|
|
1938
|
+
}
|
|
1939
|
+
function cloneSet(ae) {
|
|
1940
|
+
return new Set(ae);
|
|
1941
|
+
}
|
|
1942
|
+
function mergeSet(a, b) {
|
|
1943
|
+
b.forEach((event) => a.add(event));
|
|
1944
|
+
}
|
|
1945
|
+
function equalSet(a, b) {
|
|
1946
|
+
if (a.size != b.size)
|
|
1947
|
+
return false;
|
|
1948
|
+
for (const item of a) {
|
|
1949
|
+
if (!b.has(item))
|
|
1950
|
+
return false;
|
|
1951
|
+
}
|
|
1952
|
+
return true;
|
|
1953
|
+
}
|
|
1954
|
+
function equalMap(a, b) {
|
|
1955
|
+
if (a.size != b.size)
|
|
1956
|
+
return false;
|
|
1957
|
+
for (const [item, value] of a) {
|
|
1958
|
+
if (b.get(item) !== value)
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
return true;
|
|
1962
|
+
}
|
|
1963
|
+
function anticipatedState(node, events) {
|
|
1964
|
+
return { ant: events || new Set(), live: true, node, members: new Map() };
|
|
1965
|
+
}
|
|
1966
|
+
function cloneAnticipatedState(as) {
|
|
1967
|
+
return {
|
|
1968
|
+
ant: cloneSet(as.ant),
|
|
1969
|
+
live: as.live,
|
|
1970
|
+
node: as.node,
|
|
1971
|
+
members: new Map(as.members),
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
function mergeAnticipatedState(ae, be) {
|
|
1975
|
+
mergeSet(ae.ant, be.ant);
|
|
1976
|
+
be.members.forEach((live, block) => ae.members.set(block, live));
|
|
1977
|
+
if (be.live)
|
|
1978
|
+
ae.live = true;
|
|
1979
|
+
}
|
|
1980
|
+
function cloneAnticipatedDecls(ad) {
|
|
1981
|
+
const copy = anticipatedDecls();
|
|
1982
|
+
for (const [k, v] of ad) {
|
|
1983
|
+
if (!v.isIsolated) {
|
|
1984
|
+
copy.set(k, cloneAnticipatedState(v));
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
return copy;
|
|
1988
|
+
}
|
|
1989
|
+
function mergeAnticipatedDecls(a, b) {
|
|
1990
|
+
for (const [k, v] of b) {
|
|
1991
|
+
if (v.isIsolated)
|
|
1992
|
+
continue;
|
|
1993
|
+
const ae = a.get(k);
|
|
1994
|
+
if (ae) {
|
|
1995
|
+
mergeAnticipatedState(ae, v);
|
|
1996
|
+
}
|
|
1997
|
+
else {
|
|
1998
|
+
a.set(k, cloneAnticipatedState(v));
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
function equalStates(a, b) {
|
|
2003
|
+
if (a.size !== b.size)
|
|
2004
|
+
return false;
|
|
2005
|
+
for (const [k, ae] of a) {
|
|
2006
|
+
const be = b.get(k);
|
|
2007
|
+
if (!be ||
|
|
2008
|
+
be.live != ae.live ||
|
|
2009
|
+
be.isIsolated != ae.isIsolated ||
|
|
2010
|
+
!equalSet(ae.ant, be.ant) ||
|
|
2011
|
+
!equalMap(ae.members, be.members)) {
|
|
2012
|
+
return false;
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
return true;
|
|
2016
|
+
}
|
|
2017
|
+
const LocalRefCost = 2;
|
|
2018
|
+
function refCost(node) {
|
|
2019
|
+
if (node.type === "Literal") {
|
|
2020
|
+
switch (typeof node.value) {
|
|
2021
|
+
case "string":
|
|
2022
|
+
return 5;
|
|
2023
|
+
case "number":
|
|
2024
|
+
return 5;
|
|
2025
|
+
case "boolean":
|
|
2026
|
+
return 2;
|
|
2027
|
+
default:
|
|
2028
|
+
if (node.value === null) {
|
|
2029
|
+
return 2;
|
|
2030
|
+
}
|
|
2031
|
+
return 0;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
// A read from a non-local identifier takes 8 bytes
|
|
2035
|
+
let cost = 8;
|
|
2036
|
+
if (node.type === "Identifier")
|
|
2037
|
+
return cost;
|
|
2038
|
+
while (true) {
|
|
2039
|
+
const next = node.object;
|
|
2040
|
+
if (next.type != "MemberExpression") {
|
|
2041
|
+
if (next.type != "ThisExpression") {
|
|
2042
|
+
cost += next.type === "Identifier" && next.name === "$" ? 4 : 6;
|
|
2043
|
+
}
|
|
2044
|
+
return cost;
|
|
2045
|
+
}
|
|
2046
|
+
node = next;
|
|
2047
|
+
cost += 6;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
function defCost(node) {
|
|
2051
|
+
return refCost(node) + 2;
|
|
2052
|
+
}
|
|
2053
|
+
function candidateBoundary(candState) {
|
|
2054
|
+
const boundary = new Set();
|
|
2055
|
+
candState.members.forEach((live, block) => {
|
|
2056
|
+
if (live && block !== candState.head) {
|
|
2057
|
+
if (block.preds) {
|
|
2058
|
+
block.preds.forEach((pred) => candState.members.has(pred) || boundary.add(pred));
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
});
|
|
2062
|
+
if (candState.live) {
|
|
2063
|
+
if (!candState.head) {
|
|
2064
|
+
throw new Error(`Missing head`);
|
|
2065
|
+
}
|
|
2066
|
+
boundary.add(candState.head);
|
|
2067
|
+
}
|
|
2068
|
+
return boundary;
|
|
2069
|
+
}
|
|
2070
|
+
function candidateCost(candState) {
|
|
2071
|
+
let cost = 0;
|
|
2072
|
+
candState.ant.forEach((event) => {
|
|
2073
|
+
if (event.type === "ref") {
|
|
2074
|
+
cost -= refCost(candState.node) - LocalRefCost;
|
|
2075
|
+
}
|
|
2076
|
+
else {
|
|
2077
|
+
cost += defCost(candState.node);
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
const boundarySize = candidateBoundary(candState).size;
|
|
2081
|
+
cost += defCost(candState.node) * boundarySize;
|
|
2082
|
+
return cost;
|
|
2083
|
+
}
|
|
2084
|
+
function computeAttributes(head) {
|
|
2085
|
+
const order = getPostOrder(head);
|
|
2086
|
+
order.forEach((block, i) => {
|
|
2087
|
+
block.order = i;
|
|
2088
|
+
});
|
|
2089
|
+
if (logging) {
|
|
2090
|
+
order.forEach((block) => {
|
|
2091
|
+
console.log(block.order, `(${block.node ? block.node.loc?.start.line : "??"})`, `Preds: ${(block.preds || [])
|
|
2092
|
+
.map((block) => block.order)
|
|
2093
|
+
.join(", ")}`);
|
|
2094
|
+
if (block.events) {
|
|
2095
|
+
block.events.forEach((event) => event.type !== "exn" &&
|
|
2096
|
+
console.log(` ${event.type}: ${event.decl ? declFullName(event.decl) : "??"}`));
|
|
2097
|
+
}
|
|
2098
|
+
console.log(`Succs: ${(block.succs || [])
|
|
2099
|
+
.map((block) => block.order)
|
|
2100
|
+
.join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
const enqueued = new Set();
|
|
2104
|
+
const queue = new PriorityQueue((b, a) => (a.order || 0) - (b.order || 0));
|
|
2105
|
+
const enqueue = (block) => {
|
|
2106
|
+
if (!enqueued.has(block)) {
|
|
2107
|
+
enqueued.add(block);
|
|
2108
|
+
queue.enq(block);
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
const dequeue = () => {
|
|
2112
|
+
const block = queue.deq();
|
|
2113
|
+
enqueued.delete(block);
|
|
2114
|
+
return block;
|
|
2115
|
+
};
|
|
2116
|
+
const blockStates = [];
|
|
2117
|
+
/*
|
|
2118
|
+
Algorithm
|
|
2119
|
+
=========
|
|
2120
|
+
|
|
2121
|
+
Process blocks in post-order, and the events in reverse
|
|
2122
|
+
order to collect the AnticipatedState at the start of each
|
|
2123
|
+
Block.
|
|
2124
|
+
|
|
2125
|
+
Then for each EventDecl find the best starting block.
|
|
2126
|
+
*/
|
|
2127
|
+
const modMap = new Map();
|
|
2128
|
+
const getMod = (event, decl, id) => {
|
|
2129
|
+
if (id.type !== "Identifier" && id.type !== "MemberExpression") {
|
|
2130
|
+
throw new Error("Trying to modify a non-variable");
|
|
2131
|
+
}
|
|
2132
|
+
let eventMap = modMap.get(event);
|
|
2133
|
+
if (!eventMap) {
|
|
2134
|
+
modMap.set(event, (eventMap = new Map()));
|
|
2135
|
+
}
|
|
2136
|
+
let result = eventMap.get(decl);
|
|
2137
|
+
if (!result) {
|
|
2138
|
+
result = {
|
|
2139
|
+
type: "mod",
|
|
2140
|
+
node: event.node,
|
|
2141
|
+
decl,
|
|
2142
|
+
id,
|
|
2143
|
+
mayThrow: event.mayThrow,
|
|
675
2144
|
};
|
|
2145
|
+
eventMap.set(decl, result);
|
|
676
2146
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
2147
|
+
return result;
|
|
2148
|
+
};
|
|
2149
|
+
order.forEach((block) => enqueue(block));
|
|
2150
|
+
while (queue.size()) {
|
|
2151
|
+
const top = dequeue();
|
|
2152
|
+
if (top.order === undefined) {
|
|
2153
|
+
throw new Error(`Unreachable block was visited!`);
|
|
680
2154
|
}
|
|
681
|
-
|
|
682
|
-
|
|
2155
|
+
const curState = (top.succs &&
|
|
2156
|
+
top.succs.reduce((blockState, succ) => {
|
|
2157
|
+
const succState = blockStates[succ.order];
|
|
2158
|
+
if (succState) {
|
|
2159
|
+
if (!blockState) {
|
|
2160
|
+
blockState = cloneAnticipatedDecls(succState);
|
|
2161
|
+
}
|
|
2162
|
+
else {
|
|
2163
|
+
mergeAnticipatedDecls(blockState, succState);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return blockState;
|
|
2167
|
+
}, null)) ||
|
|
2168
|
+
anticipatedDecls();
|
|
2169
|
+
if (top.events) {
|
|
2170
|
+
for (let i = top.events.length; i--;) {
|
|
2171
|
+
const event = top.events[i];
|
|
2172
|
+
if (event.mayThrow && top.exsucc) {
|
|
2173
|
+
const succState = blockStates[top.exsucc.order];
|
|
2174
|
+
if (succState) {
|
|
2175
|
+
mergeAnticipatedDecls(curState, succState);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
switch (event.type) {
|
|
2179
|
+
case "ref": {
|
|
2180
|
+
let candidates = curState.get(event.decl);
|
|
2181
|
+
if (!candidates) {
|
|
2182
|
+
candidates = anticipatedState(event.node);
|
|
2183
|
+
curState.set(event.decl, candidates);
|
|
2184
|
+
}
|
|
2185
|
+
candidates.ant.add(event);
|
|
2186
|
+
candidates.live = true;
|
|
2187
|
+
break;
|
|
2188
|
+
}
|
|
2189
|
+
case "mod": {
|
|
2190
|
+
curState.forEach((candidates, decl) => {
|
|
2191
|
+
if (decl.type === "VariableDeclarator" &&
|
|
2192
|
+
decl.node.kind === "var" &&
|
|
2193
|
+
candidates.live) {
|
|
2194
|
+
candidates.ant.add(getMod(event, decl, candidates.node));
|
|
2195
|
+
candidates.live = false;
|
|
2196
|
+
}
|
|
2197
|
+
});
|
|
2198
|
+
break;
|
|
2199
|
+
}
|
|
2200
|
+
case "def": {
|
|
2201
|
+
let candidates = curState.get(event.decl);
|
|
2202
|
+
const isUpdate = event.node.type === "UpdateExpression" ||
|
|
2203
|
+
(event.node.type === "AssignmentExpression" &&
|
|
2204
|
+
event.node.operator !== "=");
|
|
2205
|
+
if (!candidates) {
|
|
2206
|
+
const target = event.node.type === "AssignmentExpression"
|
|
2207
|
+
? event.node.left
|
|
2208
|
+
: event.node.type === "UpdateExpression"
|
|
2209
|
+
? event.node.argument
|
|
2210
|
+
: event.node.id.type === "BinaryExpression"
|
|
2211
|
+
? event.node.id.left
|
|
2212
|
+
: event.node.id;
|
|
2213
|
+
candidates = anticipatedState(target);
|
|
2214
|
+
curState.set(event.decl, candidates);
|
|
2215
|
+
}
|
|
2216
|
+
if (isUpdate || candidates.live) {
|
|
2217
|
+
candidates.ant.add(event);
|
|
2218
|
+
}
|
|
2219
|
+
candidates.live = isUpdate;
|
|
2220
|
+
break;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
curState.forEach((antState) => {
|
|
2226
|
+
antState.head = top;
|
|
2227
|
+
antState.members.set(top, antState.live);
|
|
2228
|
+
if (!antState.live && candidateBoundary(antState).size === 0) {
|
|
2229
|
+
// we found a group that's isolated from the rest
|
|
2230
|
+
// of the function. Don't merge it with earlier
|
|
2231
|
+
// refs and defs, because we can take it or leave
|
|
2232
|
+
// it based on its own cost.
|
|
2233
|
+
antState.isIsolated = true;
|
|
2234
|
+
}
|
|
2235
|
+
});
|
|
2236
|
+
const oldState = blockStates[top.order];
|
|
2237
|
+
if (oldState && equalStates(oldState, curState)) {
|
|
2238
|
+
continue;
|
|
2239
|
+
}
|
|
2240
|
+
blockStates[top.order] = curState;
|
|
2241
|
+
if (logging) {
|
|
2242
|
+
console.log(`Updated block ${top.order}`);
|
|
2243
|
+
logAntDecls(curState);
|
|
2244
|
+
}
|
|
2245
|
+
if (top.preds) {
|
|
2246
|
+
top.preds.forEach((pred) => enqueue(pred));
|
|
683
2247
|
}
|
|
684
2248
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
2249
|
+
const candidateDecls = anticipatedDecls();
|
|
2250
|
+
blockStates.forEach((blockState, i) => {
|
|
2251
|
+
blockState &&
|
|
2252
|
+
blockState.forEach((events, decl) => {
|
|
2253
|
+
const cost = candidateCost(events);
|
|
2254
|
+
if (cost >= 0)
|
|
2255
|
+
return;
|
|
2256
|
+
const existing = candidateDecls.get(decl);
|
|
2257
|
+
if (!existing ||
|
|
2258
|
+
existing.isIsolated ||
|
|
2259
|
+
candidateCost(existing) > cost) {
|
|
2260
|
+
const boundary = candidateBoundary(events);
|
|
2261
|
+
if (!Array.from(boundary).every((block) => {
|
|
2262
|
+
if (block !== events.head && block.events) {
|
|
2263
|
+
if (events.node.type === "Literal") {
|
|
2264
|
+
return false;
|
|
2265
|
+
}
|
|
2266
|
+
let i = block.events.length;
|
|
2267
|
+
while (i--) {
|
|
2268
|
+
const event = block.events[i];
|
|
2269
|
+
if (event.type === "def" || event.type === "mod") {
|
|
2270
|
+
events.ant.add({
|
|
2271
|
+
type: "mod",
|
|
2272
|
+
node: event.node,
|
|
2273
|
+
decl,
|
|
2274
|
+
id: events.node,
|
|
2275
|
+
mayThrow: false,
|
|
2276
|
+
});
|
|
2277
|
+
events.members.set(block, false);
|
|
2278
|
+
return true;
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
const node = block.node;
|
|
2283
|
+
if (!node)
|
|
2284
|
+
return false;
|
|
2285
|
+
events.ant.add({
|
|
2286
|
+
type: "mod",
|
|
2287
|
+
node: node.type === "FunctionDeclaration" ? node.body : node,
|
|
2288
|
+
before: true,
|
|
2289
|
+
decl,
|
|
2290
|
+
id: events.node,
|
|
2291
|
+
mayThrow: false,
|
|
2292
|
+
});
|
|
2293
|
+
events.members.set(block, false);
|
|
2294
|
+
return true;
|
|
2295
|
+
})) {
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
events.live = false;
|
|
2299
|
+
if (existing && existing.isIsolated) {
|
|
2300
|
+
delete existing.isIsolated;
|
|
2301
|
+
mergeAnticipatedState(events, existing);
|
|
2302
|
+
}
|
|
2303
|
+
else if (candidateCost(events) != cost) {
|
|
2304
|
+
throw new Error(`cost of block ${i} changed`);
|
|
2305
|
+
}
|
|
2306
|
+
candidateDecls.set(decl, events);
|
|
2307
|
+
}
|
|
2308
|
+
});
|
|
2309
|
+
});
|
|
2310
|
+
if (candidateDecls.size) {
|
|
2311
|
+
return candidateDecls;
|
|
690
2312
|
}
|
|
691
|
-
|
|
692
|
-
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
693
|
-
const map = fixupLocalsMap(state);
|
|
694
|
-
const ret = processInlineBody(state, func, call, retArg, params);
|
|
695
|
-
state.localsStack[state.localsStack.length - 1].map = map;
|
|
696
|
-
return ret;
|
|
2313
|
+
return null;
|
|
697
2314
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
2315
|
+
/*
|
|
2316
|
+
* Determine the cost of fixing a def under a statement.
|
|
2317
|
+
*
|
|
2318
|
+
* eg:
|
|
2319
|
+
*
|
|
2320
|
+
* if (foo()) {
|
|
2321
|
+
* bar(X.y);
|
|
2322
|
+
* } else {
|
|
2323
|
+
* baz(X.y);
|
|
2324
|
+
* }
|
|
2325
|
+
*
|
|
2326
|
+
* Here, we could pull out X.y as a local, but if foo might modify
|
|
2327
|
+
* X.y, we have nowhere to insert the temporary. But we can rewrite
|
|
2328
|
+
* it as:
|
|
2329
|
+
*
|
|
2330
|
+
* var tmp = foo();
|
|
2331
|
+
* if (tmp) {
|
|
2332
|
+
* bar(X.y);
|
|
2333
|
+
* } else {
|
|
2334
|
+
* baz(X.y);
|
|
2335
|
+
* }
|
|
2336
|
+
*
|
|
2337
|
+
* and now we can insert a temporary before the if, but it costs
|
|
2338
|
+
* 4 bytes to do so.
|
|
2339
|
+
*
|
|
2340
|
+
* We can do the same for switch statements unless (ugh!)
|
|
2341
|
+
* the cases might modify the decl too.
|
|
2342
|
+
*
|
|
2343
|
+
* eg
|
|
2344
|
+
*
|
|
2345
|
+
* switch (foo()) {
|
|
2346
|
+
* case bar(): ...
|
|
2347
|
+
* }
|
|
2348
|
+
*
|
|
2349
|
+
*/
|
|
2350
|
+
function _isFixableStmt(node) {
|
|
2351
|
+
switch (node.type) {
|
|
2352
|
+
case "IfStatement":
|
|
2353
|
+
return 4;
|
|
2354
|
+
case "SwitchStatement":
|
|
2355
|
+
if (node.cases.every((c) => !c.test ||
|
|
2356
|
+
c.test.type === "Literal" ||
|
|
2357
|
+
c.test.type === "Identifier" ||
|
|
2358
|
+
c.test.type === "InstanceOfCase" ||
|
|
2359
|
+
(c.test.type === "UnaryExpression" && c.test.operator === ":"))) {
|
|
2360
|
+
return 4;
|
|
2361
|
+
}
|
|
2362
|
+
break;
|
|
706
2363
|
}
|
|
707
|
-
return
|
|
2364
|
+
return false;
|
|
708
2365
|
}
|
|
709
|
-
function
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
return lookupNode;
|
|
2366
|
+
function applyReplacements(func, nodeMap, declMap) {
|
|
2367
|
+
const ident = (name, node) => {
|
|
2368
|
+
return withLoc({ type: "Identifier", name }, node);
|
|
2369
|
+
};
|
|
2370
|
+
const pendingMap = new Map();
|
|
2371
|
+
const stmtStack = [func];
|
|
2372
|
+
traverseAst(func, (node) => {
|
|
2373
|
+
if (isStatement(node)) {
|
|
2374
|
+
stmtStack.push(node);
|
|
719
2375
|
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
2376
|
+
}, (node) => {
|
|
2377
|
+
const stmt = stmtStack[stmtStack.length - 1];
|
|
2378
|
+
if (stmt === node)
|
|
2379
|
+
stmtStack.pop();
|
|
2380
|
+
const events = nodeMap.get(node);
|
|
2381
|
+
if (events) {
|
|
2382
|
+
if (events.length === 1) {
|
|
2383
|
+
if (events[0].type === "ref") {
|
|
2384
|
+
if (node.type !== "Identifier" &&
|
|
2385
|
+
node.type !== "MemberExpression" &&
|
|
2386
|
+
node.type !== "Literal") {
|
|
2387
|
+
throw new Error(`Ref found, but wrong type of node: ${node.type}`);
|
|
2388
|
+
}
|
|
2389
|
+
const name = declMap.get(events[0].decl);
|
|
2390
|
+
if (!name) {
|
|
2391
|
+
throw new Error(`No replacement found for "${formatAst(node)}"`);
|
|
2392
|
+
}
|
|
2393
|
+
return ident(name, node);
|
|
2394
|
+
}
|
|
2395
|
+
else if (events[0].type === "def") {
|
|
2396
|
+
if (node.type !== "AssignmentExpression" &&
|
|
2397
|
+
node.type !== "UpdateExpression") {
|
|
2398
|
+
throw new Error(`Def found, but wrong type of node: ${node.type}`);
|
|
2399
|
+
}
|
|
2400
|
+
const target = node.type === "AssignmentExpression"
|
|
2401
|
+
? node.left
|
|
2402
|
+
: node.argument;
|
|
2403
|
+
const name = declMap.get(events[0].decl);
|
|
2404
|
+
if (!name) {
|
|
2405
|
+
throw new Error(`No replacement found for "${formatAst(target)}"`);
|
|
2406
|
+
}
|
|
2407
|
+
const id = ident(name, target);
|
|
2408
|
+
const assign = withLoc({
|
|
2409
|
+
type: "AssignmentExpression",
|
|
2410
|
+
left: target,
|
|
2411
|
+
right: { ...id },
|
|
2412
|
+
operator: "=",
|
|
2413
|
+
}, node);
|
|
2414
|
+
if (node.type === "AssignmentExpression") {
|
|
2415
|
+
node.left = id;
|
|
2416
|
+
}
|
|
2417
|
+
else {
|
|
2418
|
+
node.argument = id;
|
|
2419
|
+
}
|
|
2420
|
+
return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
events.forEach((event) => {
|
|
2424
|
+
if (event.type !== "mod") {
|
|
2425
|
+
throw new Error(`Unexpected ${event.type} found amongst multiple events`);
|
|
2426
|
+
}
|
|
2427
|
+
if (!event.decl) {
|
|
2428
|
+
throw new Error(`Unexpected null decl on mod event`);
|
|
2429
|
+
}
|
|
2430
|
+
let pending = pendingMap.get(stmt);
|
|
2431
|
+
if (!pending) {
|
|
2432
|
+
pendingMap.set(stmt, (pending = new Set()));
|
|
2433
|
+
}
|
|
2434
|
+
pending.add(event);
|
|
2435
|
+
});
|
|
745
2436
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
type: "MemberExpression",
|
|
751
|
-
object,
|
|
752
|
-
property,
|
|
753
|
-
computed: false,
|
|
754
|
-
start: node.start,
|
|
755
|
-
end: node.end,
|
|
756
|
-
loc: node.loc,
|
|
757
|
-
});
|
|
758
|
-
if (prefixes.length &&
|
|
759
|
-
prefixes[0].startsWith("$.") &&
|
|
760
|
-
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
761
|
-
const prefix = prefixes[0].split(".").slice(0, -1).reverse();
|
|
762
|
-
let found = false;
|
|
763
|
-
return prefix.reduce((current, name) => {
|
|
764
|
-
if (found)
|
|
765
|
-
return current;
|
|
766
|
-
const [, results] = state.lookup(current);
|
|
767
|
-
if (results && sameLookupResult(original, results)) {
|
|
768
|
-
found = true;
|
|
769
|
-
return current;
|
|
2437
|
+
const pending = pendingMap.get(node);
|
|
2438
|
+
if (node.type === "SequenceExpression") {
|
|
2439
|
+
if (pending) {
|
|
2440
|
+
throw new Error(`Unexpected pending list at SequenceExpression`);
|
|
770
2441
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
loc: node.loc,
|
|
777
|
-
};
|
|
778
|
-
let root = null;
|
|
779
|
-
let property = current;
|
|
780
|
-
do {
|
|
781
|
-
root = property;
|
|
782
|
-
property = property.object;
|
|
783
|
-
} while (property.type === "MemberExpression");
|
|
784
|
-
if (property.type === "ThisExpression") {
|
|
785
|
-
root.object = object;
|
|
786
|
-
return root;
|
|
2442
|
+
for (let i = node.expressions.length; i--;) {
|
|
2443
|
+
const ni = node.expressions[i];
|
|
2444
|
+
if (ni.type === "SequenceExpression") {
|
|
2445
|
+
node.expressions.splice(i, 1, ...ni.expressions);
|
|
2446
|
+
}
|
|
787
2447
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
2448
|
+
}
|
|
2449
|
+
const applyPending = (results, locNode) => {
|
|
2450
|
+
const target = results.length === 1 && results[0].type === "BlockStatement"
|
|
2451
|
+
? results[0]
|
|
2452
|
+
: null;
|
|
2453
|
+
pendingMap.delete(node);
|
|
2454
|
+
pending.forEach((event) => {
|
|
2455
|
+
const decl = event.decl;
|
|
2456
|
+
const name = declMap.get(decl);
|
|
2457
|
+
if (!name) {
|
|
2458
|
+
throw new Error(`No replacement found for "${declFullName(decl)}"`);
|
|
2459
|
+
}
|
|
2460
|
+
if (!event.id) {
|
|
2461
|
+
throw new Error(`Missing id for mod event for "${declFullName(decl)}"`);
|
|
2462
|
+
}
|
|
2463
|
+
const rhs = withLocDeep(event.id, locNode, locNode);
|
|
2464
|
+
rhs.end = rhs.start;
|
|
2465
|
+
if (rhs.loc) {
|
|
2466
|
+
rhs.loc.end = rhs.loc.start;
|
|
2467
|
+
}
|
|
2468
|
+
const insertion = withLoc({
|
|
2469
|
+
type: "ExpressionStatement",
|
|
2470
|
+
expression: withLoc({
|
|
2471
|
+
type: "AssignmentExpression",
|
|
2472
|
+
left: ident(name, rhs),
|
|
2473
|
+
right: rhs,
|
|
2474
|
+
operator: "=",
|
|
2475
|
+
}, rhs),
|
|
2476
|
+
}, rhs);
|
|
2477
|
+
if (event.type === "mod" && event.before) {
|
|
2478
|
+
if (target) {
|
|
2479
|
+
target.body.unshift(insertion);
|
|
2480
|
+
}
|
|
2481
|
+
else {
|
|
2482
|
+
results.unshift(insertion);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
else {
|
|
2486
|
+
results.push(insertion);
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2489
|
+
return results.length === 1 ? null : results;
|
|
2490
|
+
};
|
|
2491
|
+
if (node.type === "ExpressionStatement" &&
|
|
2492
|
+
node.expression.type === "SequenceExpression") {
|
|
2493
|
+
const results = [];
|
|
2494
|
+
node.expression.expressions.forEach((expression) => {
|
|
2495
|
+
results.push({ ...node, expression });
|
|
2496
|
+
});
|
|
2497
|
+
if (!pending) {
|
|
2498
|
+
return results;
|
|
2499
|
+
}
|
|
2500
|
+
return applyPending(results, node);
|
|
2501
|
+
}
|
|
2502
|
+
if (pending) {
|
|
2503
|
+
return applyPending([node], node);
|
|
2504
|
+
}
|
|
2505
|
+
return null;
|
|
2506
|
+
});
|
|
799
2507
|
}
|
|
800
2508
|
|
|
801
|
-
;// CONCATENATED MODULE: external "./util.cjs"
|
|
802
|
-
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
803
2509
|
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
804
2510
|
|
|
805
2511
|
function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
@@ -902,6 +2608,7 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
902
2608
|
|
|
903
2609
|
|
|
904
2610
|
|
|
2611
|
+
|
|
905
2612
|
function collectClassInfo(state) {
|
|
906
2613
|
const toybox = state.stack[0].decls["Toybox"][0];
|
|
907
2614
|
const lang = toybox.decls["Lang"][0];
|
|
@@ -1203,7 +2910,50 @@ function getNodeValue(node) {
|
|
|
1203
2910
|
}
|
|
1204
2911
|
return [node, type];
|
|
1205
2912
|
}
|
|
1206
|
-
function
|
|
2913
|
+
function fullTypeName(state, tsp) {
|
|
2914
|
+
if (typeof tsp.name === "string") {
|
|
2915
|
+
return tsp.name;
|
|
2916
|
+
}
|
|
2917
|
+
const [, results] = state.lookupType(tsp.name);
|
|
2918
|
+
if (results && results.length === 1 && results[0].results.length === 1) {
|
|
2919
|
+
const result = results[0].results[0];
|
|
2920
|
+
if (isStateNode(result)) {
|
|
2921
|
+
return result.fullName;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
return null;
|
|
2925
|
+
}
|
|
2926
|
+
function isBooleanExpression(state, node) {
|
|
2927
|
+
switch (node.type) {
|
|
2928
|
+
case "Literal":
|
|
2929
|
+
return typeof node.value === "boolean";
|
|
2930
|
+
case "BinaryExpression":
|
|
2931
|
+
switch (node.operator) {
|
|
2932
|
+
case "==":
|
|
2933
|
+
case "!=":
|
|
2934
|
+
case "<=":
|
|
2935
|
+
case ">=":
|
|
2936
|
+
case "<":
|
|
2937
|
+
case ">":
|
|
2938
|
+
return true;
|
|
2939
|
+
case "as":
|
|
2940
|
+
return node.right.ts.length === 1 &&
|
|
2941
|
+
node.right.ts[0].type === "TypeSpecPart" &&
|
|
2942
|
+
node.right.ts[0].name &&
|
|
2943
|
+
fullTypeName(state, node.right.ts[0]) === "$.Toybox.Lang.Boolean"
|
|
2944
|
+
? true
|
|
2945
|
+
: false;
|
|
2946
|
+
}
|
|
2947
|
+
return false;
|
|
2948
|
+
case "LogicalExpression":
|
|
2949
|
+
return (isBooleanExpression(state, node.left) &&
|
|
2950
|
+
isBooleanExpression(state, node.right));
|
|
2951
|
+
case "UnaryExpression":
|
|
2952
|
+
return node.operator === "!" && isBooleanExpression(state, node.argument);
|
|
2953
|
+
}
|
|
2954
|
+
return false;
|
|
2955
|
+
}
|
|
2956
|
+
function optimizeNode(state, node) {
|
|
1207
2957
|
switch (node.type) {
|
|
1208
2958
|
case "UnaryExpression": {
|
|
1209
2959
|
const [arg, type] = getNodeValue(node.argument);
|
|
@@ -1255,8 +3005,18 @@ function optimizeNode(node) {
|
|
|
1255
3005
|
"%": (left, right) => left % right,
|
|
1256
3006
|
"&": (left, right, type) => type === "Number" ? left & right : null,
|
|
1257
3007
|
"|": (left, right, type) => type === "Number" ? left | right : null,
|
|
3008
|
+
"^": (left, right, type) => type === "Number" ? left ^ right : null,
|
|
1258
3009
|
"<<": (left, right, type) => type === "Number" ? left << right : null,
|
|
1259
3010
|
">>": (left, right, type) => type === "Number" ? left >> right : null,
|
|
3011
|
+
"==": (left, right) => left == right,
|
|
3012
|
+
"!=": (left, right) => left != right,
|
|
3013
|
+
"<=": (left, right) => left <= right,
|
|
3014
|
+
">=": (left, right) => left >= right,
|
|
3015
|
+
"<": (left, right) => left < right,
|
|
3016
|
+
">": (left, right) => left > right,
|
|
3017
|
+
as: null,
|
|
3018
|
+
instanceof: null,
|
|
3019
|
+
has: null,
|
|
1260
3020
|
};
|
|
1261
3021
|
const op = operators[node.operator];
|
|
1262
3022
|
if (op) {
|
|
@@ -1264,11 +3024,17 @@ function optimizeNode(node) {
|
|
|
1264
3024
|
const [right, right_type] = getNodeValue(node.right);
|
|
1265
3025
|
if (!left || !right)
|
|
1266
3026
|
break;
|
|
3027
|
+
let value = null;
|
|
1267
3028
|
if (left_type != right_type ||
|
|
1268
3029
|
(left_type != "Number" && left_type != "Long")) {
|
|
1269
|
-
|
|
3030
|
+
if (node.operator !== "==" && node.operator !== "!=") {
|
|
3031
|
+
break;
|
|
3032
|
+
}
|
|
3033
|
+
value = operators[node.operator](left.value, right.value);
|
|
3034
|
+
}
|
|
3035
|
+
else {
|
|
3036
|
+
value = op(left.value, right.value, left_type);
|
|
1270
3037
|
}
|
|
1271
|
-
const value = op(left.value, right.value, left_type);
|
|
1272
3038
|
if (value === null)
|
|
1273
3039
|
break;
|
|
1274
3040
|
return {
|
|
@@ -1279,15 +3045,47 @@ function optimizeNode(node) {
|
|
|
1279
3045
|
}
|
|
1280
3046
|
break;
|
|
1281
3047
|
}
|
|
3048
|
+
case "LogicalExpression": {
|
|
3049
|
+
const [left, left_type] = getNodeValue(node.left);
|
|
3050
|
+
if (!left)
|
|
3051
|
+
break;
|
|
3052
|
+
const falsy = left.value === false ||
|
|
3053
|
+
left.value === null ||
|
|
3054
|
+
(left.value === 0 && (left_type === "Number" || left_type === "Long"));
|
|
3055
|
+
if (falsy === (node.operator === "&&")) {
|
|
3056
|
+
return left;
|
|
3057
|
+
}
|
|
3058
|
+
if (left_type !== "Boolean" &&
|
|
3059
|
+
left_type !== "Number" &&
|
|
3060
|
+
left_type !== "Long") {
|
|
3061
|
+
break;
|
|
3062
|
+
}
|
|
3063
|
+
const [right, right_type] = getNodeValue(node.right);
|
|
3064
|
+
if (right && right_type === left_type) {
|
|
3065
|
+
if (left_type === "Boolean" || node.operator === "||") {
|
|
3066
|
+
return right;
|
|
3067
|
+
}
|
|
3068
|
+
if (node.operator !== "&&") {
|
|
3069
|
+
throw new Error(`Unexpected operator "${node.operator}"`);
|
|
3070
|
+
}
|
|
3071
|
+
return { ...node, type: "BinaryExpression", operator: "&" };
|
|
3072
|
+
}
|
|
3073
|
+
if (left_type === "Boolean") {
|
|
3074
|
+
if (isBooleanExpression(state, node.right)) {
|
|
3075
|
+
return node.right;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
break;
|
|
3079
|
+
}
|
|
1282
3080
|
case "FunctionDeclaration":
|
|
1283
|
-
if (node.body && evaluateFunction(node, null) !== false) {
|
|
3081
|
+
if (node.body && evaluateFunction(state, node, null) !== false) {
|
|
1284
3082
|
node.optimizable = true;
|
|
1285
3083
|
}
|
|
1286
3084
|
break;
|
|
1287
3085
|
}
|
|
1288
3086
|
return null;
|
|
1289
3087
|
}
|
|
1290
|
-
function evaluateFunction(func, args) {
|
|
3088
|
+
function evaluateFunction(state, func, args) {
|
|
1291
3089
|
if (!func.body || (args && args.length != func.params.length)) {
|
|
1292
3090
|
return false;
|
|
1293
3091
|
}
|
|
@@ -1326,7 +3124,7 @@ function evaluateFunction(func, args) {
|
|
|
1326
3124
|
}
|
|
1327
3125
|
// fall through;
|
|
1328
3126
|
default: {
|
|
1329
|
-
const repl = optimizeNode(node);
|
|
3127
|
+
const repl = optimizeNode(state, node);
|
|
1330
3128
|
if (repl && repl.type === "Literal")
|
|
1331
3129
|
return repl;
|
|
1332
3130
|
throw new Error("Didn't optimize");
|
|
@@ -1386,10 +3184,19 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1386
3184
|
if (!objects) {
|
|
1387
3185
|
return false;
|
|
1388
3186
|
}
|
|
1389
|
-
|
|
3187
|
+
let obj = getLiteralFromDecls(objects);
|
|
1390
3188
|
if (!obj) {
|
|
1391
3189
|
return false;
|
|
1392
3190
|
}
|
|
3191
|
+
while (obj.type === "BinaryExpression") {
|
|
3192
|
+
if (obj.left.type === "BinaryExpression" && obj.left.operator === "as") {
|
|
3193
|
+
obj = { ...obj, left: obj.left.left };
|
|
3194
|
+
}
|
|
3195
|
+
else {
|
|
3196
|
+
obj = { ...obj, left: { ...obj.left } };
|
|
3197
|
+
break;
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
1393
3200
|
inPlaceReplacement(node, obj);
|
|
1394
3201
|
return true;
|
|
1395
3202
|
};
|
|
@@ -1607,14 +3414,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1607
3414
|
return null;
|
|
1608
3415
|
};
|
|
1609
3416
|
state.post = (node) => {
|
|
1610
|
-
|
|
3417
|
+
const locals = topLocals();
|
|
3418
|
+
if (locals.node === node) {
|
|
1611
3419
|
state.localsStack.pop();
|
|
1612
3420
|
}
|
|
1613
|
-
const opt = optimizeNode(node);
|
|
3421
|
+
const opt = optimizeNode(state, node);
|
|
1614
3422
|
if (opt) {
|
|
1615
3423
|
return replace(opt, node);
|
|
1616
3424
|
}
|
|
1617
3425
|
switch (node.type) {
|
|
3426
|
+
case "BlockStatement":
|
|
3427
|
+
if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
|
|
3428
|
+
node.body.splice(0, 1, ...node.body[0].body);
|
|
3429
|
+
}
|
|
3430
|
+
break;
|
|
1618
3431
|
case "ConditionalExpression":
|
|
1619
3432
|
case "IfStatement":
|
|
1620
3433
|
if (node.test.type === "Literal" &&
|
|
@@ -1624,6 +3437,12 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1624
3437
|
return false;
|
|
1625
3438
|
return replace(rep, rep);
|
|
1626
3439
|
}
|
|
3440
|
+
else if (node.type === "IfStatement" &&
|
|
3441
|
+
node.alternate &&
|
|
3442
|
+
node.alternate.type === "BlockStatement" &&
|
|
3443
|
+
!node.alternate.body.length) {
|
|
3444
|
+
delete node.alternate;
|
|
3445
|
+
}
|
|
1627
3446
|
break;
|
|
1628
3447
|
case "WhileStatement":
|
|
1629
3448
|
if (node.test.type === "Literal" && node.test.value === false) {
|
|
@@ -1651,6 +3470,48 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1651
3470
|
return { type: "Literal", value: null, raw: "null" };
|
|
1652
3471
|
}
|
|
1653
3472
|
break;
|
|
3473
|
+
case "VariableDeclaration": {
|
|
3474
|
+
const locals = topLocals();
|
|
3475
|
+
if (locals.map &&
|
|
3476
|
+
locals.node &&
|
|
3477
|
+
locals.node.type === "BlockStatement") {
|
|
3478
|
+
let results;
|
|
3479
|
+
const declarations = node.declarations;
|
|
3480
|
+
let i = 0;
|
|
3481
|
+
let j = 0;
|
|
3482
|
+
while (i < node.declarations.length) {
|
|
3483
|
+
const decl = declarations[i++];
|
|
3484
|
+
if (decl.init && decl.init.type === "CallExpression") {
|
|
3485
|
+
const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
|
|
3486
|
+
if (!inlined)
|
|
3487
|
+
continue;
|
|
3488
|
+
if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
|
|
3489
|
+
throw new Error("Unexpected inlined result");
|
|
3490
|
+
}
|
|
3491
|
+
if (!results) {
|
|
3492
|
+
results = [];
|
|
3493
|
+
}
|
|
3494
|
+
delete decl.init;
|
|
3495
|
+
results.push(withLoc({
|
|
3496
|
+
...node,
|
|
3497
|
+
declarations: declarations.slice(j, i),
|
|
3498
|
+
}, j ? declarations[j] : null, decl.id));
|
|
3499
|
+
results.push(inlined);
|
|
3500
|
+
j = i;
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
if (results) {
|
|
3504
|
+
if (j < i) {
|
|
3505
|
+
results.push({
|
|
3506
|
+
...node,
|
|
3507
|
+
declarations: declarations.slice(j, i),
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3510
|
+
return results;
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
break;
|
|
3514
|
+
}
|
|
1654
3515
|
case "ExpressionStatement":
|
|
1655
3516
|
if (node.expression.type === "CallExpression") {
|
|
1656
3517
|
return replace(optimizeCall(state, node.expression, node), node.expression);
|
|
@@ -1695,6 +3556,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1695
3556
|
});
|
|
1696
3557
|
delete state.pre;
|
|
1697
3558
|
delete state.post;
|
|
3559
|
+
state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
|
|
1698
3560
|
const cleanup = (node) => {
|
|
1699
3561
|
switch (node.type) {
|
|
1700
3562
|
case "ThisExpression":
|
|
@@ -1820,7 +3682,7 @@ function optimizeCall(state, node, context) {
|
|
|
1820
3682
|
callee.optimizable &&
|
|
1821
3683
|
!callee.hasOverride &&
|
|
1822
3684
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
1823
|
-
const ret = evaluateFunction(callee, node.arguments);
|
|
3685
|
+
const ret = evaluateFunction(state, callee, node.arguments);
|
|
1824
3686
|
if (ret) {
|
|
1825
3687
|
return ret;
|
|
1826
3688
|
}
|
|
@@ -1980,11 +3842,8 @@ async function api_getApiMapping(state, barrelList) {
|
|
|
1980
3842
|
return null;
|
|
1981
3843
|
}
|
|
1982
3844
|
}
|
|
1983
|
-
function api_hasProperty(obj, prop) {
|
|
1984
|
-
return obj ? Object.prototype.hasOwnProperty.call(obj, prop) : false;
|
|
1985
|
-
}
|
|
1986
3845
|
function api_isStateNode(node) {
|
|
1987
|
-
return
|
|
3846
|
+
return ast_hasProperty(node, "node");
|
|
1988
3847
|
}
|
|
1989
3848
|
function api_variableDeclarationName(node) {
|
|
1990
3849
|
return ("left" in node ? node.left : node).name;
|
|
@@ -2004,7 +3863,7 @@ function checkOne(state, ns, decls, node, isStatic) {
|
|
|
2004
3863
|
}
|
|
2005
3864
|
return cls.superClass.reduce((result, sup) => {
|
|
2006
3865
|
const sdecls = sup[decls];
|
|
2007
|
-
const next =
|
|
3866
|
+
const next = ast_hasProperty(sdecls, node.name)
|
|
2008
3867
|
? sdecls[node.name]
|
|
2009
3868
|
: superChain(sup);
|
|
2010
3869
|
return next ? (result ? result.concat(next) : next) : result;
|
|
@@ -2029,7 +3888,7 @@ function checkOne(state, ns, decls, node, isStatic) {
|
|
|
2029
3888
|
};
|
|
2030
3889
|
if (api_isStateNode(ns)) {
|
|
2031
3890
|
const ndecls = ns[decls];
|
|
2032
|
-
if (
|
|
3891
|
+
if (ast_hasProperty(ndecls, node.name)) {
|
|
2033
3892
|
return ndecls[node.name];
|
|
2034
3893
|
}
|
|
2035
3894
|
switch (ns.type) {
|
|
@@ -2309,7 +4168,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2309
4168
|
if (name) {
|
|
2310
4169
|
if (!parent.decls)
|
|
2311
4170
|
parent.decls = {};
|
|
2312
|
-
if (
|
|
4171
|
+
if (ast_hasProperty(parent.decls, name)) {
|
|
2313
4172
|
const what = node.type == "ModuleDeclaration" ? "type" : "node";
|
|
2314
4173
|
const e = parent.decls[name].find((d) => api_isStateNode(d) && d[what] == elm[what]);
|
|
2315
4174
|
if (e != null) {
|
|
@@ -2335,7 +4194,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2335
4194
|
elm.decls = { [name]: [elm] };
|
|
2336
4195
|
if (!parent.type_decls)
|
|
2337
4196
|
parent.type_decls = {};
|
|
2338
|
-
if (!
|
|
4197
|
+
if (!ast_hasProperty(parent.type_decls, name)) {
|
|
2339
4198
|
parent.type_decls[name] = [];
|
|
2340
4199
|
}
|
|
2341
4200
|
parent.type_decls[name].push(elm);
|
|
@@ -2359,7 +4218,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2359
4218
|
const [parent] = state.stack.slice(-1);
|
|
2360
4219
|
if (!parent.type_decls)
|
|
2361
4220
|
parent.type_decls = {};
|
|
2362
|
-
if (!
|
|
4221
|
+
if (!ast_hasProperty(parent.type_decls, name)) {
|
|
2363
4222
|
parent.type_decls[name] = [];
|
|
2364
4223
|
}
|
|
2365
4224
|
else if (parent.type_decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == node)) {
|
|
@@ -2383,7 +4242,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2383
4242
|
const stack = state.stackClone();
|
|
2384
4243
|
node.declarations.forEach((decl) => {
|
|
2385
4244
|
const name = api_variableDeclarationName(decl.id);
|
|
2386
|
-
if (!
|
|
4245
|
+
if (!ast_hasProperty(decls, name)) {
|
|
2387
4246
|
decls[name] = [];
|
|
2388
4247
|
}
|
|
2389
4248
|
else if (decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == decl)) {
|
|
@@ -2398,7 +4257,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2398
4257
|
stack,
|
|
2399
4258
|
});
|
|
2400
4259
|
if (node.kind == "const") {
|
|
2401
|
-
if (!
|
|
4260
|
+
if (!ast_hasProperty(state.index, name)) {
|
|
2402
4261
|
state.index[name] = [];
|
|
2403
4262
|
}
|
|
2404
4263
|
(0,external_util_cjs_namespaceObject.pushUnique)(state.index[name], parent);
|
|
@@ -2444,11 +4303,11 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2444
4303
|
prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.test(init.raw)) {
|
|
2445
4304
|
prev = init.value;
|
|
2446
4305
|
}
|
|
2447
|
-
if (!
|
|
4306
|
+
if (!ast_hasProperty(values, name)) {
|
|
2448
4307
|
values[name] = [];
|
|
2449
4308
|
}
|
|
2450
4309
|
(0,external_util_cjs_namespaceObject.pushUnique)(values[name], m);
|
|
2451
|
-
if (!
|
|
4310
|
+
if (!ast_hasProperty(state.index, name)) {
|
|
2452
4311
|
state.index[name] = [];
|
|
2453
4312
|
}
|
|
2454
4313
|
(0,external_util_cjs_namespaceObject.pushUnique)(state.index[name], parent);
|
|
@@ -2588,7 +4447,7 @@ function findUsing(state, stack, using) {
|
|
|
2588
4447
|
find(node.object);
|
|
2589
4448
|
name = node.property.name;
|
|
2590
4449
|
}
|
|
2591
|
-
if (
|
|
4450
|
+
if (ast_hasProperty(module.decls, name)) {
|
|
2592
4451
|
const decls = module.decls[name];
|
|
2593
4452
|
if (decls &&
|
|
2594
4453
|
decls.length === 1 &&
|
|
@@ -2611,7 +4470,7 @@ function findUsing(state, stack, using) {
|
|
|
2611
4470
|
function findUsingForNode(state, stack, i, node, isType) {
|
|
2612
4471
|
while (i >= 0) {
|
|
2613
4472
|
const si = stack[i--];
|
|
2614
|
-
if (
|
|
4473
|
+
if (ast_hasProperty(si.usings, node.name)) {
|
|
2615
4474
|
const using = si.usings[node.name];
|
|
2616
4475
|
const module = findUsing(state, stack, using);
|
|
2617
4476
|
return module && [module];
|
|
@@ -2621,7 +4480,7 @@ function findUsingForNode(state, stack, i, node, isType) {
|
|
|
2621
4480
|
const using = si.imports[j];
|
|
2622
4481
|
const module = findUsing(state, stack, using);
|
|
2623
4482
|
if (module) {
|
|
2624
|
-
if (
|
|
4483
|
+
if (ast_hasProperty(module.type_decls, node.name)) {
|
|
2625
4484
|
return module.type_decls[node.name];
|
|
2626
4485
|
}
|
|
2627
4486
|
}
|
|
@@ -2631,6 +4490,8 @@ function findUsingForNode(state, stack, i, node, isType) {
|
|
|
2631
4490
|
return null;
|
|
2632
4491
|
}
|
|
2633
4492
|
|
|
4493
|
+
})();
|
|
4494
|
+
|
|
2634
4495
|
var __webpack_export_target__ = exports;
|
|
2635
4496
|
for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i];
|
|
2636
4497
|
if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });
|