@markw65/monkeyc-optimizer 1.0.28 → 1.0.29
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 +27 -0
- package/build/api.cjs +2042 -223
- package/build/optimizer.cjs +1965 -169
- 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");
|
|
@@ -514,7 +783,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
514
783
|
if (hasProperty(params, node.name)) {
|
|
515
784
|
const ix = params[node.name];
|
|
516
785
|
if (ix >= 0) {
|
|
517
|
-
replacement = call.arguments[ix];
|
|
786
|
+
replacement = { ...call.arguments[ix] };
|
|
518
787
|
replacements.add(replacement);
|
|
519
788
|
return replacement;
|
|
520
789
|
}
|
|
@@ -553,6 +822,10 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
553
822
|
}
|
|
554
823
|
}
|
|
555
824
|
function inliner_unused(expression, top) {
|
|
825
|
+
const estmt = (expression) => withLoc({
|
|
826
|
+
type: "ExpressionStatement",
|
|
827
|
+
expression,
|
|
828
|
+
}, expression);
|
|
556
829
|
switch (expression.type) {
|
|
557
830
|
case "Literal":
|
|
558
831
|
return [];
|
|
@@ -564,9 +837,50 @@ function inliner_unused(expression, top) {
|
|
|
564
837
|
if (expression.operator === "as") {
|
|
565
838
|
return inliner_unused(expression.left);
|
|
566
839
|
}
|
|
567
|
-
// fall through
|
|
568
|
-
case "LogicalExpression":
|
|
569
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
|
+
}
|
|
570
884
|
case "UnaryExpression":
|
|
571
885
|
return inliner_unused(expression.argument);
|
|
572
886
|
case "MemberExpression":
|
|
@@ -581,17 +895,7 @@ function inliner_unused(expression, top) {
|
|
|
581
895
|
.map((p) => inliner_unused(p.key).concat(inliner_unused(p.value)))
|
|
582
896
|
.flat(1);
|
|
583
897
|
}
|
|
584
|
-
return top
|
|
585
|
-
? null
|
|
586
|
-
: [
|
|
587
|
-
{
|
|
588
|
-
type: "ExpressionStatement",
|
|
589
|
-
expression,
|
|
590
|
-
start: expression.start,
|
|
591
|
-
end: expression.end,
|
|
592
|
-
loc: expression.loc,
|
|
593
|
-
},
|
|
594
|
-
];
|
|
898
|
+
return top ? null : [estmt(expression)];
|
|
595
899
|
}
|
|
596
900
|
function inliner_diagnostic(state, loc, message, type = "INFO") {
|
|
597
901
|
if (!loc || !loc.source)
|
|
@@ -639,7 +943,9 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
639
943
|
if (retStmtCount > 1) {
|
|
640
944
|
inlineDiagnostic(state, func, call, "Function had more than one return statement");
|
|
641
945
|
}
|
|
642
|
-
else if (context.type === "AssignmentExpression"
|
|
946
|
+
else if ((context.type === "AssignmentExpression" ||
|
|
947
|
+
context.type === "VariableDeclarator") &&
|
|
948
|
+
retStmtCount !== 1) {
|
|
643
949
|
inlineDiagnostic(state, func, call, "Function did not have a return statement");
|
|
644
950
|
return null;
|
|
645
951
|
}
|
|
@@ -647,7 +953,9 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
647
953
|
const last = func.node.body.body.slice(-1)[0];
|
|
648
954
|
if (!last ||
|
|
649
955
|
last.type !== "ReturnStatement" ||
|
|
650
|
-
(context.type === "AssignmentExpression"
|
|
956
|
+
((context.type === "AssignmentExpression" ||
|
|
957
|
+
context.type === "VariableDeclarator") &&
|
|
958
|
+
!last.argument)) {
|
|
651
959
|
inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
|
|
652
960
|
return null;
|
|
653
961
|
}
|
|
@@ -671,139 +979,1495 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
671
979
|
if (last.type != "ReturnStatement") {
|
|
672
980
|
throw new Error("ReturnStatement got lost!");
|
|
673
981
|
}
|
|
674
|
-
if (
|
|
675
|
-
context.
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
982
|
+
if (last.argument) {
|
|
983
|
+
if (context.type === "AssignmentExpression") {
|
|
984
|
+
context.right = last.argument;
|
|
985
|
+
body.body[body.body.length - 1] = {
|
|
986
|
+
type: "ExpressionStatement",
|
|
987
|
+
expression: context,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
else if (context.type === "VariableDeclarator") {
|
|
991
|
+
const { id, init: _init, kind: _kind, ...rest } = context;
|
|
992
|
+
body.body[body.body.length - 1] = {
|
|
993
|
+
...rest,
|
|
994
|
+
type: "ExpressionStatement",
|
|
995
|
+
expression: {
|
|
996
|
+
...rest,
|
|
997
|
+
type: "AssignmentExpression",
|
|
998
|
+
operator: "=",
|
|
999
|
+
left: id.type === "Identifier" ? id : id.left,
|
|
1000
|
+
right: last.argument,
|
|
1001
|
+
},
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
const side_exprs = inliner_unused(last.argument);
|
|
1006
|
+
body.body.splice(body.body.length - 1, 1, ...side_exprs);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
--body.body.length;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return body;
|
|
1014
|
+
}
|
|
1015
|
+
function inliner_inlineFunction(state, func, call, context) {
|
|
1016
|
+
if (context) {
|
|
1017
|
+
return inlineWithArgs(state, func, call, context);
|
|
1018
|
+
}
|
|
1019
|
+
const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
|
|
1020
|
+
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
1021
|
+
const map = fixupLocalsMap(state);
|
|
1022
|
+
const ret = processInlineBody(state, func, call, retArg, params);
|
|
1023
|
+
state.localsStack[state.localsStack.length - 1].map = map;
|
|
1024
|
+
return ret;
|
|
1025
|
+
}
|
|
1026
|
+
function applyTypeIfNeeded(node) {
|
|
1027
|
+
if ("enumType" in node && node.enumType) {
|
|
1028
|
+
node = {
|
|
1029
|
+
type: "BinaryExpression",
|
|
1030
|
+
operator: "as",
|
|
1031
|
+
left: node,
|
|
1032
|
+
right: { type: "TypeSpecList", ts: [node.enumType] },
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
return node;
|
|
1036
|
+
}
|
|
1037
|
+
function fixNodeScope(state, lookupNode, nodeStack) {
|
|
1038
|
+
if (lookupNode.type === "Identifier") {
|
|
1039
|
+
const locals = state.localsStack[state.localsStack.length - 1];
|
|
1040
|
+
const { map } = locals;
|
|
1041
|
+
if (!map)
|
|
1042
|
+
throw new Error("No local variable map!");
|
|
1043
|
+
if (hasProperty(map, lookupNode.name) && map[lookupNode.name] !== false) {
|
|
1044
|
+
// map[name] !== false means its an entry that was created during inlining
|
|
1045
|
+
// so its definitely one of our locals.
|
|
1046
|
+
return lookupNode;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
const [, original] = state.lookup(lookupNode, null, nodeStack);
|
|
1050
|
+
if (!original) {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
const [, current] = state.lookup(lookupNode);
|
|
1054
|
+
// For now, leave it alone if it already maps to the same thing.
|
|
1055
|
+
// With a bit more work, we could find the guaranteed shortest
|
|
1056
|
+
// reference, and then use this to optimize *all* symbols, not
|
|
1057
|
+
// just fix inlined ones.
|
|
1058
|
+
if (current && sameLookupResult(original, current)) {
|
|
1059
|
+
return lookupNode;
|
|
1060
|
+
}
|
|
1061
|
+
const node = lookupNode.type === "Identifier"
|
|
1062
|
+
? lookupNode
|
|
1063
|
+
: lookupNode.property;
|
|
1064
|
+
if (original.length === 1 &&
|
|
1065
|
+
original[0].results.length === 1 &&
|
|
1066
|
+
original[0].results[0].type === "EnumStringMember") {
|
|
1067
|
+
return applyTypeIfNeeded(original[0].results[0].init);
|
|
1068
|
+
}
|
|
1069
|
+
const prefixes = original
|
|
1070
|
+
.map((lookupDef) => lookupDef.results.map((sn) => {
|
|
1071
|
+
if (isStateNode(sn) && sn.fullName) {
|
|
1072
|
+
return sn.fullName;
|
|
1073
|
+
}
|
|
1074
|
+
return "";
|
|
1075
|
+
}))
|
|
1076
|
+
.flat();
|
|
1077
|
+
const member = (object, property) => ({
|
|
1078
|
+
type: "MemberExpression",
|
|
1079
|
+
object,
|
|
1080
|
+
property,
|
|
1081
|
+
computed: false,
|
|
1082
|
+
start: node.start,
|
|
1083
|
+
end: node.end,
|
|
1084
|
+
loc: node.loc,
|
|
1085
|
+
});
|
|
1086
|
+
if (prefixes.length &&
|
|
1087
|
+
prefixes[0].startsWith("$.") &&
|
|
1088
|
+
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
1089
|
+
const prefix = prefixes[0].split(".").slice(0, -1).reverse();
|
|
1090
|
+
let found = false;
|
|
1091
|
+
return prefix.reduce((current, name) => {
|
|
1092
|
+
if (found)
|
|
1093
|
+
return current;
|
|
1094
|
+
const [, results] = state.lookup(current);
|
|
1095
|
+
if (results && sameLookupResult(original, results)) {
|
|
1096
|
+
found = true;
|
|
1097
|
+
return current;
|
|
1098
|
+
}
|
|
1099
|
+
const object = {
|
|
1100
|
+
type: "Identifier",
|
|
1101
|
+
name,
|
|
1102
|
+
start: node.start,
|
|
1103
|
+
end: node.end,
|
|
1104
|
+
loc: node.loc,
|
|
1105
|
+
};
|
|
1106
|
+
let root = null;
|
|
1107
|
+
let property = current;
|
|
1108
|
+
do {
|
|
1109
|
+
root = property;
|
|
1110
|
+
property = property.object;
|
|
1111
|
+
} while (property.type === "MemberExpression");
|
|
1112
|
+
if (property.type === "ThisExpression") {
|
|
1113
|
+
root.object = object;
|
|
1114
|
+
return root;
|
|
1115
|
+
}
|
|
1116
|
+
root.object = member(object, property);
|
|
1117
|
+
return current;
|
|
1118
|
+
}, member({
|
|
1119
|
+
type: "ThisExpression",
|
|
1120
|
+
text: "self",
|
|
1121
|
+
start: node.start,
|
|
1122
|
+
end: node.end,
|
|
1123
|
+
loc: node.loc,
|
|
1124
|
+
}, node));
|
|
1125
|
+
}
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
;// CONCATENATED MODULE: external "./util.cjs"
|
|
1130
|
+
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
1131
|
+
;// CONCATENATED MODULE: ./src/control-flow.ts
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
const Terminals = {
|
|
1135
|
+
BreakStatement: "break",
|
|
1136
|
+
ContinueStatement: "continue",
|
|
1137
|
+
ReturnStatement: null,
|
|
1138
|
+
ThrowStatement: "throw",
|
|
1139
|
+
};
|
|
1140
|
+
class LocalState {
|
|
1141
|
+
constructor(func) {
|
|
1142
|
+
this.stack = [];
|
|
1143
|
+
this.info = new Map();
|
|
1144
|
+
this.curBlock = {};
|
|
1145
|
+
this.unreachable = false;
|
|
1146
|
+
this.push(func);
|
|
1147
|
+
}
|
|
1148
|
+
push(node) {
|
|
1149
|
+
const top = { node };
|
|
1150
|
+
this.stack.push(top);
|
|
1151
|
+
return top;
|
|
1152
|
+
}
|
|
1153
|
+
pop() {
|
|
1154
|
+
return this.stack.pop();
|
|
1155
|
+
}
|
|
1156
|
+
top(depth) {
|
|
1157
|
+
return this.stack[this.stack.length - (depth || 1)];
|
|
1158
|
+
}
|
|
1159
|
+
addEdge(from, to) {
|
|
1160
|
+
if (!from.succs) {
|
|
1161
|
+
from.succs = [to];
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
pushUnique(from.succs, to);
|
|
1165
|
+
}
|
|
1166
|
+
if (!to.preds) {
|
|
1167
|
+
to.preds = [from];
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
pushUnique(to.preds, from);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
newBlock(block) {
|
|
1174
|
+
if (!block)
|
|
1175
|
+
block = {};
|
|
1176
|
+
if (!this.unreachable) {
|
|
1177
|
+
this.addEdge(this.curBlock, block);
|
|
1178
|
+
}
|
|
1179
|
+
this.unreachable = false;
|
|
1180
|
+
for (let i = this.stack.length; i--;) {
|
|
1181
|
+
const si = this.stack[i];
|
|
1182
|
+
if (si.throw) {
|
|
1183
|
+
block.exsucc = si.throw;
|
|
1184
|
+
if (!si.throw.expreds) {
|
|
1185
|
+
si.throw.expreds = [block];
|
|
1186
|
+
}
|
|
1187
|
+
else {
|
|
1188
|
+
si.throw.expreds.push(block);
|
|
1189
|
+
}
|
|
1190
|
+
break;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
return (this.curBlock = block);
|
|
1194
|
+
}
|
|
1195
|
+
terminal(type) {
|
|
1196
|
+
const re = Terminals[type];
|
|
1197
|
+
if (re) {
|
|
1198
|
+
for (let i = this.stack.length; i--;) {
|
|
1199
|
+
const target = this.stack[i][re];
|
|
1200
|
+
if (target) {
|
|
1201
|
+
this.addEdge(this.curBlock, target);
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
this.unreachable = true;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
function control_flow_buildReducedGraph(state, func, notice) {
|
|
1210
|
+
const { stack, pre, post } = state;
|
|
1211
|
+
try {
|
|
1212
|
+
const localState = new LocalState(func.node);
|
|
1213
|
+
const ret = localState.curBlock;
|
|
1214
|
+
state.stack = func.stack;
|
|
1215
|
+
const stmtStack = [func.node];
|
|
1216
|
+
let tryActive = 0;
|
|
1217
|
+
state.pre = (node) => {
|
|
1218
|
+
if (state.inType || localState.unreachable) {
|
|
1219
|
+
return [];
|
|
1220
|
+
}
|
|
1221
|
+
if (!localState.curBlock.node &&
|
|
1222
|
+
(isStatement(node) || isExpression(node))) {
|
|
1223
|
+
localState.curBlock.node = node;
|
|
1224
|
+
}
|
|
1225
|
+
if (isStatement(node)) {
|
|
1226
|
+
stmtStack.push(node);
|
|
1227
|
+
}
|
|
1228
|
+
switch (node.type) {
|
|
1229
|
+
case "AttributeList":
|
|
1230
|
+
return [];
|
|
1231
|
+
case "SwitchStatement": {
|
|
1232
|
+
const top = localState.push(node);
|
|
1233
|
+
top.break = {};
|
|
1234
|
+
state.traverse(node.discriminant);
|
|
1235
|
+
const testBlocks = [];
|
|
1236
|
+
let defaultSeen = false;
|
|
1237
|
+
node.cases.forEach((sc, i) => {
|
|
1238
|
+
if (sc.test) {
|
|
1239
|
+
state.traverse(sc.test);
|
|
1240
|
+
testBlocks[i] = localState.curBlock;
|
|
1241
|
+
localState.newBlock();
|
|
1242
|
+
}
|
|
1243
|
+
else {
|
|
1244
|
+
defaultSeen = true;
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
const endOfTests = localState.curBlock;
|
|
1248
|
+
if (!defaultSeen) {
|
|
1249
|
+
localState.addEdge(endOfTests, top.break);
|
|
1250
|
+
}
|
|
1251
|
+
localState.unreachable = true;
|
|
1252
|
+
node.cases.forEach((sc, i) => {
|
|
1253
|
+
localState.newBlock();
|
|
1254
|
+
localState.addEdge(testBlocks[i] || endOfTests, localState.curBlock);
|
|
1255
|
+
sc.consequent.every((s) => {
|
|
1256
|
+
state.traverse(s);
|
|
1257
|
+
return !localState.unreachable;
|
|
1258
|
+
});
|
|
1259
|
+
});
|
|
1260
|
+
localState.newBlock(top.break);
|
|
1261
|
+
localState.unreachable = !top.break.preds;
|
|
1262
|
+
return [];
|
|
1263
|
+
}
|
|
1264
|
+
case "DoWhileStatement":
|
|
1265
|
+
case "WhileStatement": {
|
|
1266
|
+
localState.push(node);
|
|
1267
|
+
const top = localState.top();
|
|
1268
|
+
top.break = {};
|
|
1269
|
+
top.continue = {};
|
|
1270
|
+
let head;
|
|
1271
|
+
if (node.type === "WhileStatement") {
|
|
1272
|
+
head = localState.newBlock(top.continue);
|
|
1273
|
+
state.traverse(node.test);
|
|
1274
|
+
localState.addEdge(localState.newBlock(), top.break);
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
head = localState.newBlock();
|
|
1278
|
+
}
|
|
1279
|
+
state.traverse(node.body);
|
|
1280
|
+
if (node.type === "DoWhileStatement") {
|
|
1281
|
+
localState.newBlock(top.continue);
|
|
1282
|
+
state.traverse(node.test);
|
|
1283
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
1284
|
+
}
|
|
1285
|
+
localState.addEdge(localState.curBlock, head);
|
|
1286
|
+
localState.curBlock = top.break;
|
|
1287
|
+
return [];
|
|
1288
|
+
}
|
|
1289
|
+
case "TryStatement": {
|
|
1290
|
+
const top = localState.push(node);
|
|
1291
|
+
const catches = (top.throw = {});
|
|
1292
|
+
// This edge shouldn't exist, but we can trigger
|
|
1293
|
+
// (incorrect) "variable may not be initialized" errors
|
|
1294
|
+
// in the monkey c compiler without it.
|
|
1295
|
+
// https://forums.garmin.com/developer/connect-iq/i/bug-reports/incorrect-maybe-uninitialized-error
|
|
1296
|
+
localState.addEdge(localState.curBlock, top.throw);
|
|
1297
|
+
localState.newBlock();
|
|
1298
|
+
tryActive++;
|
|
1299
|
+
state.traverse(node.block);
|
|
1300
|
+
tryActive--;
|
|
1301
|
+
delete top.throw;
|
|
1302
|
+
top.posttry = {};
|
|
1303
|
+
const tryFallsThrough = !localState.unreachable;
|
|
1304
|
+
if (node.finalizer) {
|
|
1305
|
+
tryActive++;
|
|
1306
|
+
top.throw = top.finally = {};
|
|
1307
|
+
// curBlock branches to finally, no matter how it exits.
|
|
1308
|
+
localState.addEdge(localState.curBlock, top.finally);
|
|
1309
|
+
}
|
|
1310
|
+
else {
|
|
1311
|
+
if (!localState.unreachable) {
|
|
1312
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
localState.unreachable = true;
|
|
1316
|
+
localState.newBlock(catches);
|
|
1317
|
+
if (node.handler) {
|
|
1318
|
+
state.traverse(node.handler);
|
|
1319
|
+
if (top.throw) {
|
|
1320
|
+
tryActive--;
|
|
1321
|
+
delete top.throw;
|
|
1322
|
+
}
|
|
1323
|
+
// Each "catch (ex instanceof Foo)" chains to the next,
|
|
1324
|
+
// but "catch (ex)" terminates the list. If the end
|
|
1325
|
+
// of the chain has a predecessor, its possible that
|
|
1326
|
+
// none of the conditions matched, so the exception
|
|
1327
|
+
// will propagate from there.
|
|
1328
|
+
if (localState.curBlock.preds) {
|
|
1329
|
+
localState.terminal("ThrowStatement");
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
if (top.throw) {
|
|
1333
|
+
tryActive--;
|
|
1334
|
+
delete top.throw;
|
|
1335
|
+
}
|
|
1336
|
+
if (node.finalizer) {
|
|
1337
|
+
localState.unreachable = true;
|
|
1338
|
+
localState.newBlock(top.finally);
|
|
1339
|
+
delete top.finally;
|
|
1340
|
+
state.traverse(node.finalizer);
|
|
1341
|
+
if (tryFallsThrough && !localState.unreachable) {
|
|
1342
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
1343
|
+
}
|
|
1344
|
+
localState.terminal("ThrowStatement");
|
|
1345
|
+
}
|
|
1346
|
+
localState.unreachable = true;
|
|
1347
|
+
localState.newBlock(top.posttry);
|
|
1348
|
+
return [];
|
|
1349
|
+
}
|
|
1350
|
+
case "CatchClause": {
|
|
1351
|
+
const top = localState.top();
|
|
1352
|
+
if (!localState.curBlock.preds && !localState.curBlock.expreds) {
|
|
1353
|
+
return [];
|
|
1354
|
+
}
|
|
1355
|
+
const next = {};
|
|
1356
|
+
if (node.param && node.param.type === "BinaryExpression") {
|
|
1357
|
+
state.traverse(node.param);
|
|
1358
|
+
localState.addEdge(localState.curBlock, next);
|
|
1359
|
+
localState.newBlock();
|
|
1360
|
+
}
|
|
1361
|
+
state.traverse(node.body);
|
|
1362
|
+
if (top.finally) {
|
|
1363
|
+
// this edge exists even if this point is unreachable
|
|
1364
|
+
localState.addEdge(localState.curBlock, top.finally);
|
|
1365
|
+
}
|
|
1366
|
+
if (!localState.unreachable) {
|
|
1367
|
+
if (!top.posttry)
|
|
1368
|
+
top.posttry = {};
|
|
1369
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
1370
|
+
}
|
|
1371
|
+
localState.unreachable = true;
|
|
1372
|
+
localState.newBlock(next);
|
|
1373
|
+
return [];
|
|
1374
|
+
}
|
|
1375
|
+
case "ForStatement": {
|
|
1376
|
+
const top = localState.push(node);
|
|
1377
|
+
if (node.init)
|
|
1378
|
+
state.traverse(node.init);
|
|
1379
|
+
const head = localState.newBlock();
|
|
1380
|
+
top.break = {};
|
|
1381
|
+
top.continue = {};
|
|
1382
|
+
if (node.test) {
|
|
1383
|
+
state.traverse(node.test);
|
|
1384
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
1385
|
+
localState.newBlock();
|
|
1386
|
+
}
|
|
1387
|
+
state.traverse(node.body);
|
|
1388
|
+
localState.newBlock(top.continue);
|
|
1389
|
+
if (node.update) {
|
|
1390
|
+
state.traverse(node.update);
|
|
1391
|
+
}
|
|
1392
|
+
if (!localState.unreachable) {
|
|
1393
|
+
localState.addEdge(localState.curBlock, head);
|
|
1394
|
+
}
|
|
1395
|
+
// there is no fall through from the end of the loop
|
|
1396
|
+
// to the next block. The only way there is via break
|
|
1397
|
+
// or the test failing.
|
|
1398
|
+
localState.unreachable = true;
|
|
1399
|
+
localState.newBlock(top.break);
|
|
1400
|
+
if (!top.break.preds) {
|
|
1401
|
+
localState.unreachable = true;
|
|
1402
|
+
}
|
|
1403
|
+
return [];
|
|
1404
|
+
}
|
|
1405
|
+
case "IfStatement":
|
|
1406
|
+
case "ConditionalExpression": {
|
|
1407
|
+
state.traverse(node.test);
|
|
1408
|
+
const alternate = {};
|
|
1409
|
+
localState.addEdge(localState.curBlock, alternate);
|
|
1410
|
+
localState.newBlock();
|
|
1411
|
+
state.traverse(node.consequent);
|
|
1412
|
+
const consequent = localState.unreachable
|
|
1413
|
+
? null
|
|
1414
|
+
: localState.curBlock;
|
|
1415
|
+
localState.unreachable = true;
|
|
1416
|
+
localState.newBlock(alternate);
|
|
1417
|
+
if (node.alternate) {
|
|
1418
|
+
state.traverse(node.alternate);
|
|
1419
|
+
if (!localState.unreachable) {
|
|
1420
|
+
localState.newBlock();
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (consequent) {
|
|
1424
|
+
if (localState.unreachable) {
|
|
1425
|
+
localState.newBlock();
|
|
1426
|
+
}
|
|
1427
|
+
localState.addEdge(consequent, localState.curBlock);
|
|
1428
|
+
}
|
|
1429
|
+
return [];
|
|
1430
|
+
}
|
|
1431
|
+
case "LogicalExpression": {
|
|
1432
|
+
state.traverse(node.left);
|
|
1433
|
+
if (localState.unreachable)
|
|
1434
|
+
break;
|
|
1435
|
+
const mid = localState.curBlock;
|
|
1436
|
+
localState.newBlock();
|
|
1437
|
+
state.traverse(node.right);
|
|
1438
|
+
localState.newBlock();
|
|
1439
|
+
localState.addEdge(mid, localState.curBlock);
|
|
1440
|
+
return [];
|
|
1441
|
+
}
|
|
1442
|
+
case "VariableDeclarator":
|
|
1443
|
+
return ["init"];
|
|
1444
|
+
case "MemberExpression":
|
|
1445
|
+
if (!node.computed) {
|
|
1446
|
+
return ["object"];
|
|
1447
|
+
}
|
|
1448
|
+
break;
|
|
1449
|
+
case "UnaryExpression":
|
|
1450
|
+
if (node.operator === ":") {
|
|
1451
|
+
return [];
|
|
1452
|
+
}
|
|
1453
|
+
break;
|
|
1454
|
+
case "UpdateExpression":
|
|
1455
|
+
// We don't want to traverse the argument, since then it would
|
|
1456
|
+
// look like a ref, rather than a def. But if its a
|
|
1457
|
+
// MemberExpression, we *do* want to traverse the subexpressions
|
|
1458
|
+
// as potential refs.
|
|
1459
|
+
if (node.argument.type === "MemberExpression") {
|
|
1460
|
+
state.traverse(node.argument.object);
|
|
1461
|
+
if (node.argument.computed) {
|
|
1462
|
+
state.traverse(node.argument.property);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return [];
|
|
1466
|
+
case "AssignmentExpression":
|
|
1467
|
+
if (node.left.type === "MemberExpression") {
|
|
1468
|
+
state.traverse(node.left.object);
|
|
1469
|
+
if (node.left.computed) {
|
|
1470
|
+
state.traverse(node.left.property);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return ["right"];
|
|
1474
|
+
case "ThrowStatement":
|
|
1475
|
+
case "ReturnStatement":
|
|
1476
|
+
if (node.argument) {
|
|
1477
|
+
state.traverse(node.argument);
|
|
1478
|
+
}
|
|
1479
|
+
// fall through
|
|
1480
|
+
case "BreakStatement":
|
|
1481
|
+
case "ContinueStatement":
|
|
1482
|
+
localState.terminal(node.type);
|
|
1483
|
+
return [];
|
|
1484
|
+
}
|
|
1485
|
+
return null;
|
|
1486
|
+
};
|
|
1487
|
+
const addEvent = (block, event) => {
|
|
1488
|
+
if (!block.events) {
|
|
1489
|
+
block.events = [event];
|
|
1490
|
+
}
|
|
1491
|
+
else {
|
|
1492
|
+
block.events.push(event);
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
state.post = (node) => {
|
|
1496
|
+
const curStmt = stmtStack[stmtStack.length - 1];
|
|
1497
|
+
if (!state.inType) {
|
|
1498
|
+
const throws = tryActive > 0 && mayThrow(node);
|
|
1499
|
+
const event = notice(node, curStmt, throws);
|
|
1500
|
+
if (throws) {
|
|
1501
|
+
if (!event) {
|
|
1502
|
+
throw new Error("mayThrow expression in try/catch must generate an event");
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
else if (event) {
|
|
1506
|
+
event.mayThrow = false;
|
|
1507
|
+
}
|
|
1508
|
+
if (event) {
|
|
1509
|
+
if (event.mayThrow) {
|
|
1510
|
+
for (let i = localState.stack.length; i--;) {
|
|
1511
|
+
const target = localState.stack[i].throw;
|
|
1512
|
+
if (target) {
|
|
1513
|
+
if (localState.curBlock.exsucc) {
|
|
1514
|
+
if (localState.curBlock.exsucc !== target) {
|
|
1515
|
+
throw new Error(`Block has multiple throw targets`);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
else {
|
|
1519
|
+
localState.curBlock.exsucc = target;
|
|
1520
|
+
if (!target.expreds) {
|
|
1521
|
+
target.expreds = [localState.curBlock];
|
|
1522
|
+
}
|
|
1523
|
+
else {
|
|
1524
|
+
target.expreds.push(localState.curBlock);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
break;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
addEvent(localState.curBlock, event);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
if (curStmt === node) {
|
|
1535
|
+
stmtStack.pop();
|
|
1536
|
+
}
|
|
1537
|
+
if (localState.top().node === node) {
|
|
1538
|
+
localState.pop();
|
|
1539
|
+
}
|
|
1540
|
+
return null;
|
|
1541
|
+
};
|
|
1542
|
+
state.traverse(func.node);
|
|
1543
|
+
return cleanCfg(ret);
|
|
1544
|
+
}
|
|
1545
|
+
finally {
|
|
1546
|
+
state.pre = pre;
|
|
1547
|
+
state.post = post;
|
|
1548
|
+
state.stack = stack;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
function cleanCfg(head) {
|
|
1552
|
+
preOrderTraverse(head, (cur) => {
|
|
1553
|
+
if (cur.succs && cur.succs.length === 1) {
|
|
1554
|
+
const succ = cur.succs[0];
|
|
1555
|
+
if (succ !== head &&
|
|
1556
|
+
succ.preds.length === 1 &&
|
|
1557
|
+
(!cur.exsucc || cur.exsucc === succ.exsucc) &&
|
|
1558
|
+
(!succ.succs ||
|
|
1559
|
+
succ.succs.length === 1 ||
|
|
1560
|
+
(cur.preds && cur.preds.length === 1))) {
|
|
1561
|
+
if (cur.events) {
|
|
1562
|
+
if (succ.events) {
|
|
1563
|
+
cur.events.push(...succ.events);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
else if (succ.events) {
|
|
1567
|
+
cur.events = succ.events;
|
|
1568
|
+
}
|
|
1569
|
+
if (succ.exsucc) {
|
|
1570
|
+
const preds = succ.exsucc.expreds;
|
|
1571
|
+
for (let i = preds.length; i--;) {
|
|
1572
|
+
if (preds[i] === succ) {
|
|
1573
|
+
// If cur has an exsucc, we already
|
|
1574
|
+
// checked that its the same as succ's,
|
|
1575
|
+
// so we can just delete the edge.
|
|
1576
|
+
// Otherwise, we need to point it at cur.
|
|
1577
|
+
if (cur.exsucc) {
|
|
1578
|
+
preds.splice(i, 1);
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
preds[i] = cur;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
cur.exsucc = succ.exsucc;
|
|
1587
|
+
cur.succs = succ.succs;
|
|
1588
|
+
if (cur.succs) {
|
|
1589
|
+
cur.succs.forEach((s) => s.preds.forEach((p, i, arr) => {
|
|
1590
|
+
if (p === succ) {
|
|
1591
|
+
arr[i] = cur;
|
|
1592
|
+
}
|
|
1593
|
+
}));
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
return head;
|
|
1599
|
+
}
|
|
1600
|
+
function postOrderTraverse(head, visitor) {
|
|
1601
|
+
const visited = new Set();
|
|
1602
|
+
const helper = (cur) => {
|
|
1603
|
+
if (visited.has(cur))
|
|
1604
|
+
return;
|
|
1605
|
+
visited.add(cur);
|
|
1606
|
+
if (cur.succs) {
|
|
1607
|
+
cur.succs.forEach((block) => helper(block));
|
|
1608
|
+
}
|
|
1609
|
+
if (cur.exsucc)
|
|
1610
|
+
helper(cur.exsucc);
|
|
1611
|
+
visitor(cur);
|
|
1612
|
+
};
|
|
1613
|
+
helper(head);
|
|
1614
|
+
}
|
|
1615
|
+
function preOrderTraverse(head, visitor) {
|
|
1616
|
+
const visited = new Set();
|
|
1617
|
+
const helper = (cur) => {
|
|
1618
|
+
if (visited.has(cur))
|
|
1619
|
+
return;
|
|
1620
|
+
visited.add(cur);
|
|
1621
|
+
visitor(cur);
|
|
1622
|
+
if (cur.succs) {
|
|
1623
|
+
cur.succs.forEach((block) => helper(block));
|
|
1624
|
+
}
|
|
1625
|
+
if (cur.exsucc)
|
|
1626
|
+
helper(cur.exsucc);
|
|
1627
|
+
};
|
|
1628
|
+
helper(head);
|
|
1629
|
+
}
|
|
1630
|
+
function control_flow_getPostOrder(head) {
|
|
1631
|
+
const blocks = [];
|
|
1632
|
+
postOrderTraverse(head, (block) => blocks.push(block));
|
|
1633
|
+
return blocks;
|
|
1634
|
+
}
|
|
1635
|
+
function getPreOrder(head) {
|
|
1636
|
+
const blocks = [];
|
|
1637
|
+
postOrderTraverse(head, (block) => blocks.push(block));
|
|
1638
|
+
return blocks;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
|
|
1642
|
+
var priorityqueuejs = __webpack_require__(2789);
|
|
1643
|
+
;// CONCATENATED MODULE: ./src/pre.ts
|
|
1644
|
+
|
|
1645
|
+
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* This implements a pseudo Partial Redundancy Elimination
|
|
1650
|
+
* pass. It isn't quite like traditional PRE because we're
|
|
1651
|
+
* aiming to minimize size, not dynamic instructions. So
|
|
1652
|
+
* for us, its worthwhile to take something like:
|
|
1653
|
+
*
|
|
1654
|
+
* switch (x) {
|
|
1655
|
+
* case 1: foo(A.B); break;
|
|
1656
|
+
* case 2: foo(C); break;
|
|
1657
|
+
* case 3: bar(A.B); break;
|
|
1658
|
+
* }
|
|
1659
|
+
*
|
|
1660
|
+
* and rewrite it as
|
|
1661
|
+
*
|
|
1662
|
+
* var tmp = A.B;
|
|
1663
|
+
* switch (x) {
|
|
1664
|
+
* case 1: foo(tmp); break;
|
|
1665
|
+
* case 2: foo(C); break;
|
|
1666
|
+
* case 3: bar(tmp); break;
|
|
1667
|
+
* }
|
|
1668
|
+
*
|
|
1669
|
+
* because even though A.B wasn't used on all paths where we
|
|
1670
|
+
* inserted the temporary, we still reduced the code size.
|
|
1671
|
+
*/
|
|
1672
|
+
const logging = false;
|
|
1673
|
+
function declFullName(decl) {
|
|
1674
|
+
switch (decl.type) {
|
|
1675
|
+
case "Literal":
|
|
1676
|
+
return decl.raw || decl.value?.toString() || "null";
|
|
1677
|
+
case "VariableDeclarator":
|
|
1678
|
+
return decl.fullName;
|
|
1679
|
+
default:
|
|
1680
|
+
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
function declName(decl) {
|
|
1684
|
+
switch (decl.type) {
|
|
1685
|
+
case "Literal":
|
|
1686
|
+
return (decl.raw || decl.value?.toString() || "null").replace(/[^\w]/g, "_");
|
|
1687
|
+
case "VariableDeclarator":
|
|
1688
|
+
return decl.name;
|
|
1689
|
+
default:
|
|
1690
|
+
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
function pre_sizeBasedPRE(state, func) {
|
|
1694
|
+
if (!func.node.body)
|
|
1695
|
+
return;
|
|
1696
|
+
if (!state.config ||
|
|
1697
|
+
!state.config.sizeBasedPRE ||
|
|
1698
|
+
(typeof state.config.sizeBasedPRE === "string" &&
|
|
1699
|
+
state.config.sizeBasedPRE !== func.fullName)) {
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
1703
|
+
const candidates = computeAttributes(head);
|
|
1704
|
+
if (candidates) {
|
|
1705
|
+
if (logging) {
|
|
1706
|
+
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
1707
|
+
candidates.forEach((s, decl) => {
|
|
1708
|
+
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
1709
|
+
if (event.type === "def")
|
|
1710
|
+
defs++;
|
|
1711
|
+
return defs;
|
|
1712
|
+
}, 0);
|
|
1713
|
+
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live`);
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
const nodeMap = new Map();
|
|
1717
|
+
const declMap = new Map();
|
|
1718
|
+
const variableDecl = withLoc({
|
|
1719
|
+
type: "VariableDeclaration",
|
|
1720
|
+
declarations: [],
|
|
1721
|
+
kind: "var",
|
|
1722
|
+
}, func.node.body);
|
|
1723
|
+
variableDecl.end = variableDecl.start;
|
|
1724
|
+
variableDecl.loc.end = variableDecl.loc.start;
|
|
1725
|
+
candidates.forEach((s, decl) => {
|
|
1726
|
+
let name;
|
|
1727
|
+
let i = 0;
|
|
1728
|
+
do {
|
|
1729
|
+
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
1730
|
+
if (!identifiers.has(name))
|
|
1731
|
+
break;
|
|
1732
|
+
i++;
|
|
1733
|
+
} while (true);
|
|
1734
|
+
declMap.set(decl, name);
|
|
1735
|
+
variableDecl.declarations.push(withLoc({
|
|
1736
|
+
type: "VariableDeclarator",
|
|
1737
|
+
id: withLoc({ type: "Identifier", name }, variableDecl),
|
|
1738
|
+
kind: "var",
|
|
1739
|
+
}, variableDecl));
|
|
1740
|
+
s.ant.forEach((event) => {
|
|
1741
|
+
const events = nodeMap.get(event.node);
|
|
1742
|
+
if (!events) {
|
|
1743
|
+
nodeMap.set(event.node, [event]);
|
|
1744
|
+
}
|
|
1745
|
+
else {
|
|
1746
|
+
events.push(event);
|
|
1747
|
+
}
|
|
1748
|
+
});
|
|
1749
|
+
});
|
|
1750
|
+
applyReplacements(func.node, nodeMap, declMap);
|
|
1751
|
+
func.node.body.body.unshift(variableDecl);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
function unhandledExpression(node) {
|
|
1755
|
+
throw new Error(`Unhandled expression type: ${node.type}`);
|
|
1756
|
+
}
|
|
1757
|
+
function buildPREGraph(state, func) {
|
|
1758
|
+
const findDecl = (node) => {
|
|
1759
|
+
if (node.type === "Identifier" ||
|
|
1760
|
+
(node.type === "MemberExpression" && !node.computed)) {
|
|
1761
|
+
const [, results] = state.lookup(node);
|
|
1762
|
+
if (results &&
|
|
1763
|
+
results.length === 1 &&
|
|
1764
|
+
results[0].parent?.type != "BlockStatement" &&
|
|
1765
|
+
results[0].results.length === 1 &&
|
|
1766
|
+
results[0].results[0].type === "VariableDeclarator") {
|
|
1767
|
+
return results[0].results[0];
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
return null;
|
|
1771
|
+
};
|
|
1772
|
+
const literals = new Map();
|
|
1773
|
+
const identifiers = new Set();
|
|
1774
|
+
const liveDefs = new Map();
|
|
1775
|
+
const liveStmts = new Map();
|
|
1776
|
+
const liveDef = (def, stmt) => {
|
|
1777
|
+
let curNodes = liveDefs.get(def);
|
|
1778
|
+
if (!curNodes) {
|
|
1779
|
+
liveDefs.set(def, (curNodes = new Set()));
|
|
1780
|
+
}
|
|
1781
|
+
curNodes.add(stmt);
|
|
1782
|
+
let defs = liveStmts.get(stmt);
|
|
1783
|
+
if (!defs) {
|
|
1784
|
+
liveStmts.set(stmt, (defs = new Map()));
|
|
1785
|
+
}
|
|
1786
|
+
defs.set(def, (defs.get(def) || 0) + 1);
|
|
1787
|
+
};
|
|
1788
|
+
return {
|
|
1789
|
+
identifiers,
|
|
1790
|
+
graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
|
|
1791
|
+
const defs = liveStmts.get(node);
|
|
1792
|
+
if (defs) {
|
|
1793
|
+
liveStmts.delete(node);
|
|
1794
|
+
defs.forEach((count, def) => {
|
|
1795
|
+
if (count > 1) {
|
|
1796
|
+
defs.set(def, count--);
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
const v = liveDefs.get(def);
|
|
1800
|
+
if (!v || !v.has(node)) {
|
|
1801
|
+
throw new Error(`No stmt in liveDef for ${def ? declFullName(def) : "null"}`);
|
|
1802
|
+
}
|
|
1803
|
+
v.delete(node);
|
|
1804
|
+
if (!v.size) {
|
|
1805
|
+
liveDefs.delete(def);
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
switch (node.type) {
|
|
1810
|
+
case "BinaryExpression":
|
|
1811
|
+
case "UnaryExpression":
|
|
1812
|
+
case "SizedArrayExpression":
|
|
1813
|
+
case "ArrayExpression":
|
|
1814
|
+
case "ObjectExpression":
|
|
1815
|
+
case "ThisExpression":
|
|
1816
|
+
case "LogicalExpression":
|
|
1817
|
+
case "ConditionalExpression":
|
|
1818
|
+
case "SequenceExpression":
|
|
1819
|
+
case "ParenthesizedExpression":
|
|
1820
|
+
break;
|
|
1821
|
+
case "Literal":
|
|
1822
|
+
if (!node.value && refCost(node) > LocalRefCost) {
|
|
1823
|
+
let decl = literals.get(node.value);
|
|
1824
|
+
if (!decl) {
|
|
1825
|
+
decl = node;
|
|
1826
|
+
literals.set(node.value, decl);
|
|
1827
|
+
}
|
|
1828
|
+
return {
|
|
1829
|
+
type: "ref",
|
|
1830
|
+
node,
|
|
1831
|
+
decl: decl,
|
|
1832
|
+
mayThrow,
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
break;
|
|
1836
|
+
case "Identifier":
|
|
1837
|
+
identifiers.add(node.name);
|
|
1838
|
+
// fall through
|
|
1839
|
+
case "MemberExpression":
|
|
1840
|
+
{
|
|
1841
|
+
const decl = findDecl(node);
|
|
1842
|
+
if (decl && decl.type === "VariableDeclarator") {
|
|
1843
|
+
const defStmts = (decl.node.kind === "var" && liveDefs.get(null)) ||
|
|
1844
|
+
liveDefs.get(decl);
|
|
1845
|
+
if (defStmts) {
|
|
1846
|
+
break;
|
|
1847
|
+
/*
|
|
1848
|
+
// hold off on this for now. we need to communicate
|
|
1849
|
+
// which defs need to be fixed, which involves yet-another
|
|
1850
|
+
// table.
|
|
1851
|
+
|
|
1852
|
+
if (defStmts.size !== 1) break;
|
|
1853
|
+
const fixable = isFixableStmt([...defStmts][0]);
|
|
1854
|
+
if (fixable === false) break;
|
|
1855
|
+
cost += fixable;
|
|
1856
|
+
*/
|
|
1857
|
+
}
|
|
1858
|
+
return {
|
|
1859
|
+
type: "ref",
|
|
1860
|
+
node,
|
|
1861
|
+
decl,
|
|
1862
|
+
mayThrow,
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
break;
|
|
1867
|
+
case "VariableDeclarator": {
|
|
1868
|
+
const decl = findDecl(node.id.type === "BinaryExpression" ? node.id.left : node.id);
|
|
1869
|
+
if (decl) {
|
|
1870
|
+
liveDef(decl, stmt);
|
|
1871
|
+
return {
|
|
1872
|
+
type: "def",
|
|
1873
|
+
node,
|
|
1874
|
+
decl,
|
|
1875
|
+
mayThrow,
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
break;
|
|
1879
|
+
}
|
|
1880
|
+
case "AssignmentExpression": {
|
|
1881
|
+
const decl = findDecl(node.left);
|
|
1882
|
+
if (decl) {
|
|
1883
|
+
liveDef(decl, stmt);
|
|
1884
|
+
return {
|
|
1885
|
+
type: "def",
|
|
1886
|
+
node,
|
|
1887
|
+
decl,
|
|
1888
|
+
mayThrow,
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
break;
|
|
1892
|
+
}
|
|
1893
|
+
case "UpdateExpression": {
|
|
1894
|
+
const decl = findDecl(node.argument);
|
|
1895
|
+
if (decl) {
|
|
1896
|
+
liveDef(decl, stmt);
|
|
1897
|
+
return {
|
|
1898
|
+
type: "def",
|
|
1899
|
+
node,
|
|
1900
|
+
decl,
|
|
1901
|
+
mayThrow,
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
break;
|
|
1905
|
+
}
|
|
1906
|
+
case "NewExpression":
|
|
1907
|
+
case "CallExpression":
|
|
1908
|
+
liveDef(null, stmt);
|
|
1909
|
+
return { type: "mod", node, mayThrow };
|
|
1910
|
+
default:
|
|
1911
|
+
if (!isExpression(node))
|
|
1912
|
+
break;
|
|
1913
|
+
unhandledExpression(node);
|
|
1914
|
+
}
|
|
1915
|
+
if (mayThrow) {
|
|
1916
|
+
return { type: "exn", node, mayThrow };
|
|
1917
|
+
}
|
|
1918
|
+
return null;
|
|
1919
|
+
}),
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
function anticipatedDecls() {
|
|
1923
|
+
return new Map();
|
|
1924
|
+
}
|
|
1925
|
+
function cloneSet(ae) {
|
|
1926
|
+
return new Set(ae);
|
|
1927
|
+
}
|
|
1928
|
+
function mergeSet(a, b) {
|
|
1929
|
+
b.forEach((event) => a.add(event));
|
|
1930
|
+
}
|
|
1931
|
+
function equalSet(a, b) {
|
|
1932
|
+
if (a.size != b.size)
|
|
1933
|
+
return false;
|
|
1934
|
+
for (const item of a) {
|
|
1935
|
+
if (!b.has(item))
|
|
1936
|
+
return false;
|
|
1937
|
+
}
|
|
1938
|
+
return true;
|
|
1939
|
+
}
|
|
1940
|
+
function equalMap(a, b) {
|
|
1941
|
+
if (a.size != b.size)
|
|
1942
|
+
return false;
|
|
1943
|
+
for (const [item, value] of a) {
|
|
1944
|
+
if (b.get(item) !== value)
|
|
1945
|
+
return false;
|
|
1946
|
+
}
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
function anticipatedState(node, events) {
|
|
1950
|
+
return { ant: events || new Set(), live: true, node, members: new Map() };
|
|
1951
|
+
}
|
|
1952
|
+
function cloneAnticipatedState(as) {
|
|
1953
|
+
return {
|
|
1954
|
+
ant: cloneSet(as.ant),
|
|
1955
|
+
live: as.live,
|
|
1956
|
+
node: as.node,
|
|
1957
|
+
members: new Map(as.members),
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
function cloneAnticipatedDecls(ad) {
|
|
1961
|
+
const copy = anticipatedDecls();
|
|
1962
|
+
for (const [k, v] of ad) {
|
|
1963
|
+
copy.set(k, cloneAnticipatedState(v));
|
|
1964
|
+
}
|
|
1965
|
+
return copy;
|
|
1966
|
+
}
|
|
1967
|
+
function mergeAnticipatedDecls(a, b) {
|
|
1968
|
+
for (const [k, v] of b) {
|
|
1969
|
+
const ae = a.get(k);
|
|
1970
|
+
if (ae) {
|
|
1971
|
+
mergeSet(ae.ant, v.ant);
|
|
1972
|
+
v.members.forEach((live, block) => ae.members.set(block, live));
|
|
1973
|
+
if (v.live)
|
|
1974
|
+
ae.live = true;
|
|
1975
|
+
}
|
|
1976
|
+
else {
|
|
1977
|
+
a.set(k, cloneAnticipatedState(v));
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
function equalStates(a, b) {
|
|
1982
|
+
if (a.size !== b.size)
|
|
1983
|
+
return false;
|
|
1984
|
+
for (const [k, ae] of a) {
|
|
1985
|
+
const be = b.get(k);
|
|
1986
|
+
if (!be ||
|
|
1987
|
+
be.live != ae.live ||
|
|
1988
|
+
!equalSet(ae.ant, be.ant) ||
|
|
1989
|
+
!equalMap(ae.members, be.members)) {
|
|
1990
|
+
return false;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
return true;
|
|
1994
|
+
}
|
|
1995
|
+
const LocalRefCost = 2;
|
|
1996
|
+
function refCost(node) {
|
|
1997
|
+
if (node.type === "Literal") {
|
|
1998
|
+
switch (typeof node.value) {
|
|
1999
|
+
case "string":
|
|
2000
|
+
return 5;
|
|
2001
|
+
case "number":
|
|
2002
|
+
return 5;
|
|
2003
|
+
case "boolean":
|
|
2004
|
+
return 2;
|
|
2005
|
+
default:
|
|
2006
|
+
if (node.value === null) {
|
|
2007
|
+
return 2;
|
|
2008
|
+
}
|
|
2009
|
+
return 0;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
// A read from a non-local identifier takes 8 bytes
|
|
2013
|
+
let cost = 8;
|
|
2014
|
+
if (node.type === "Identifier")
|
|
2015
|
+
return cost;
|
|
2016
|
+
while (true) {
|
|
2017
|
+
const next = node.object;
|
|
2018
|
+
if (next.type != "MemberExpression") {
|
|
2019
|
+
if (next.type != "ThisExpression") {
|
|
2020
|
+
cost += next.type === "Identifier" && next.name === "$" ? 4 : 6;
|
|
2021
|
+
}
|
|
2022
|
+
return cost;
|
|
2023
|
+
}
|
|
2024
|
+
node = next;
|
|
2025
|
+
cost += 6;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
function defCost(node) {
|
|
2029
|
+
return refCost(node) + 2;
|
|
2030
|
+
}
|
|
2031
|
+
function candidateBoundary(candState) {
|
|
2032
|
+
const boundary = new Set();
|
|
2033
|
+
candState.members.forEach((live, block) => {
|
|
2034
|
+
if (live && block !== candState.head) {
|
|
2035
|
+
if (block.preds) {
|
|
2036
|
+
block.preds.forEach((pred) => candState.members.has(pred) || boundary.add(pred));
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
});
|
|
2040
|
+
if (candState.live) {
|
|
2041
|
+
if (!candState.head) {
|
|
2042
|
+
throw new Error(`Missing head`);
|
|
2043
|
+
}
|
|
2044
|
+
boundary.add(candState.head);
|
|
2045
|
+
}
|
|
2046
|
+
return boundary;
|
|
2047
|
+
}
|
|
2048
|
+
function candidateCost(candState) {
|
|
2049
|
+
let cost = 0;
|
|
2050
|
+
candState.ant.forEach((event) => {
|
|
2051
|
+
if (event.type === "ref") {
|
|
2052
|
+
cost -= refCost(candState.node) - LocalRefCost;
|
|
2053
|
+
}
|
|
2054
|
+
else {
|
|
2055
|
+
cost += defCost(candState.node);
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
cost += defCost(candState.node) * candidateBoundary(candState).size;
|
|
2059
|
+
return cost;
|
|
2060
|
+
}
|
|
2061
|
+
function computeAttributes(head) {
|
|
2062
|
+
const order = getPostOrder(head);
|
|
2063
|
+
order.forEach((block, i) => {
|
|
2064
|
+
block.order = i;
|
|
2065
|
+
});
|
|
2066
|
+
if (logging) {
|
|
2067
|
+
order.forEach((block) => {
|
|
2068
|
+
console.log(block.order, `(${block.node ? block.node.loc?.start.line : "??"})`, `Preds: ${(block.preds || [])
|
|
2069
|
+
.map((block) => block.order)
|
|
2070
|
+
.join(", ")}`);
|
|
2071
|
+
if (block.events) {
|
|
2072
|
+
block.events.forEach((event) => event.type !== "exn" &&
|
|
2073
|
+
console.log(` ${event.type}: ${event.decl ? declFullName(event.decl) : "??"}`));
|
|
2074
|
+
}
|
|
2075
|
+
console.log(`Succs: ${(block.succs || [])
|
|
2076
|
+
.map((block) => block.order)
|
|
2077
|
+
.join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
const enqueued = new Set();
|
|
2081
|
+
const queue = new PriorityQueue((b, a) => (a.order || 0) - (b.order || 0));
|
|
2082
|
+
const enqueue = (block) => {
|
|
2083
|
+
if (!enqueued.has(block)) {
|
|
2084
|
+
enqueued.add(block);
|
|
2085
|
+
queue.enq(block);
|
|
2086
|
+
}
|
|
2087
|
+
};
|
|
2088
|
+
const dequeue = () => {
|
|
2089
|
+
const block = queue.deq();
|
|
2090
|
+
enqueued.delete(block);
|
|
2091
|
+
return block;
|
|
2092
|
+
};
|
|
2093
|
+
const blockStates = [];
|
|
2094
|
+
/*
|
|
2095
|
+
Algorithm
|
|
2096
|
+
=========
|
|
2097
|
+
|
|
2098
|
+
Process blocks in post-order, and the events in reverse
|
|
2099
|
+
order to collect the AnticipatedState at the start of each
|
|
2100
|
+
Block.
|
|
2101
|
+
|
|
2102
|
+
Then for each EventDecl find the best starting block.
|
|
2103
|
+
*/
|
|
2104
|
+
const modMap = new Map();
|
|
2105
|
+
const getMod = (event, decl, id) => {
|
|
2106
|
+
if (id.type !== "Identifier" && id.type !== "MemberExpression") {
|
|
2107
|
+
throw new Error("Trying to modify a non-variable");
|
|
2108
|
+
}
|
|
2109
|
+
let eventMap = modMap.get(event);
|
|
2110
|
+
if (!eventMap) {
|
|
2111
|
+
modMap.set(event, (eventMap = new Map()));
|
|
2112
|
+
}
|
|
2113
|
+
let result = eventMap.get(decl);
|
|
2114
|
+
if (!result) {
|
|
2115
|
+
result = {
|
|
2116
|
+
type: "mod",
|
|
2117
|
+
node: event.node,
|
|
2118
|
+
decl,
|
|
2119
|
+
id,
|
|
2120
|
+
mayThrow: event.mayThrow,
|
|
679
2121
|
};
|
|
2122
|
+
eventMap.set(decl, result);
|
|
680
2123
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
2124
|
+
return result;
|
|
2125
|
+
};
|
|
2126
|
+
order.forEach((block) => enqueue(block));
|
|
2127
|
+
while (queue.size()) {
|
|
2128
|
+
const top = dequeue();
|
|
2129
|
+
if (top.order === undefined) {
|
|
2130
|
+
throw new Error(`Unreachable block was visited!`);
|
|
684
2131
|
}
|
|
685
|
-
|
|
686
|
-
|
|
2132
|
+
const curState = (top.succs &&
|
|
2133
|
+
top.succs.reduce((blockState, succ) => {
|
|
2134
|
+
const succState = blockStates[succ.order];
|
|
2135
|
+
if (succState) {
|
|
2136
|
+
if (!blockState) {
|
|
2137
|
+
blockState = cloneAnticipatedDecls(succState);
|
|
2138
|
+
}
|
|
2139
|
+
else {
|
|
2140
|
+
mergeAnticipatedDecls(blockState, succState);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
return blockState;
|
|
2144
|
+
}, null)) ||
|
|
2145
|
+
anticipatedDecls();
|
|
2146
|
+
if (top.events) {
|
|
2147
|
+
for (let i = top.events.length; i--;) {
|
|
2148
|
+
const event = top.events[i];
|
|
2149
|
+
if (event.mayThrow && top.exsucc) {
|
|
2150
|
+
const succState = blockStates[top.exsucc.order];
|
|
2151
|
+
if (succState) {
|
|
2152
|
+
mergeAnticipatedDecls(curState, succState);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
switch (event.type) {
|
|
2156
|
+
case "ref": {
|
|
2157
|
+
let candidates = curState.get(event.decl);
|
|
2158
|
+
if (!candidates) {
|
|
2159
|
+
candidates = anticipatedState(event.node);
|
|
2160
|
+
curState.set(event.decl, candidates);
|
|
2161
|
+
}
|
|
2162
|
+
candidates.ant.add(event);
|
|
2163
|
+
candidates.live = true;
|
|
2164
|
+
break;
|
|
2165
|
+
}
|
|
2166
|
+
case "mod": {
|
|
2167
|
+
curState.forEach((candidates, decl) => {
|
|
2168
|
+
if (decl.type === "VariableDeclarator" &&
|
|
2169
|
+
decl.node.kind === "var" &&
|
|
2170
|
+
candidates.live) {
|
|
2171
|
+
candidates.ant.add(getMod(event, decl, candidates.node));
|
|
2172
|
+
candidates.live = false;
|
|
2173
|
+
}
|
|
2174
|
+
});
|
|
2175
|
+
break;
|
|
2176
|
+
}
|
|
2177
|
+
case "def": {
|
|
2178
|
+
let candidates = curState.get(event.decl);
|
|
2179
|
+
const isUpdate = event.node.type === "UpdateExpression" ||
|
|
2180
|
+
(event.node.type === "AssignmentExpression" &&
|
|
2181
|
+
event.node.operator !== "=");
|
|
2182
|
+
if (!candidates) {
|
|
2183
|
+
const target = event.node.type === "AssignmentExpression"
|
|
2184
|
+
? event.node.left
|
|
2185
|
+
: event.node.type === "UpdateExpression"
|
|
2186
|
+
? event.node.argument
|
|
2187
|
+
: event.node.id.type === "BinaryExpression"
|
|
2188
|
+
? event.node.id.left
|
|
2189
|
+
: event.node.id;
|
|
2190
|
+
candidates = anticipatedState(target);
|
|
2191
|
+
curState.set(event.decl, candidates);
|
|
2192
|
+
}
|
|
2193
|
+
if (isUpdate || candidates.live) {
|
|
2194
|
+
candidates.ant.add(event);
|
|
2195
|
+
}
|
|
2196
|
+
if (!isUpdate) {
|
|
2197
|
+
candidates.live = false;
|
|
2198
|
+
}
|
|
2199
|
+
break;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
curState.forEach((antState) => {
|
|
2205
|
+
antState.head = top;
|
|
2206
|
+
antState.members.set(top, antState.live);
|
|
2207
|
+
});
|
|
2208
|
+
const oldState = blockStates[top.order];
|
|
2209
|
+
if (oldState && equalStates(oldState, curState)) {
|
|
2210
|
+
continue;
|
|
2211
|
+
}
|
|
2212
|
+
blockStates[top.order] = curState;
|
|
2213
|
+
if (top.preds) {
|
|
2214
|
+
top.preds.forEach((pred) => enqueue(pred));
|
|
687
2215
|
}
|
|
688
2216
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
2217
|
+
const candidateDecls = anticipatedDecls();
|
|
2218
|
+
blockStates.forEach((blockState, i) => {
|
|
2219
|
+
blockState &&
|
|
2220
|
+
blockState.forEach((events, decl) => {
|
|
2221
|
+
const cost = candidateCost(events);
|
|
2222
|
+
if (cost >= 0)
|
|
2223
|
+
return;
|
|
2224
|
+
const existing = candidateDecls.get(decl);
|
|
2225
|
+
if (!existing || candidateCost(existing) > cost) {
|
|
2226
|
+
const boundary = candidateBoundary(events);
|
|
2227
|
+
if (!Array.from(boundary).every((block) => {
|
|
2228
|
+
if (block !== events.head && block.events) {
|
|
2229
|
+
if (events.node.type === "Literal") {
|
|
2230
|
+
return false;
|
|
2231
|
+
}
|
|
2232
|
+
let i = block.events.length;
|
|
2233
|
+
while (i--) {
|
|
2234
|
+
const event = block.events[i];
|
|
2235
|
+
if (event.type === "def" || event.type === "mod") {
|
|
2236
|
+
events.ant.add({
|
|
2237
|
+
type: "mod",
|
|
2238
|
+
node: event.node,
|
|
2239
|
+
decl,
|
|
2240
|
+
id: events.node,
|
|
2241
|
+
mayThrow: false,
|
|
2242
|
+
});
|
|
2243
|
+
events.members.set(block, false);
|
|
2244
|
+
return true;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
const node = block.node;
|
|
2249
|
+
if (!node)
|
|
2250
|
+
return false;
|
|
2251
|
+
events.ant.add({
|
|
2252
|
+
type: "mod",
|
|
2253
|
+
node: node.type === "FunctionDeclaration" ? node.body : node,
|
|
2254
|
+
before: true,
|
|
2255
|
+
decl,
|
|
2256
|
+
id: events.node,
|
|
2257
|
+
mayThrow: false,
|
|
2258
|
+
});
|
|
2259
|
+
events.members.set(block, false);
|
|
2260
|
+
return true;
|
|
2261
|
+
})) {
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
events.live = false;
|
|
2265
|
+
if (candidateCost(events) != cost) {
|
|
2266
|
+
throw new Error(`cost of block ${i} changed`);
|
|
2267
|
+
}
|
|
2268
|
+
candidateDecls.set(decl, events);
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2271
|
+
});
|
|
2272
|
+
if (candidateDecls.size) {
|
|
2273
|
+
return candidateDecls;
|
|
694
2274
|
}
|
|
695
|
-
|
|
696
|
-
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
697
|
-
const map = fixupLocalsMap(state);
|
|
698
|
-
const ret = processInlineBody(state, func, call, retArg, params);
|
|
699
|
-
state.localsStack[state.localsStack.length - 1].map = map;
|
|
700
|
-
return ret;
|
|
2275
|
+
return null;
|
|
701
2276
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
2277
|
+
/*
|
|
2278
|
+
* Determine the cost of fixing a def under a statement.
|
|
2279
|
+
*
|
|
2280
|
+
* eg:
|
|
2281
|
+
*
|
|
2282
|
+
* if (foo()) {
|
|
2283
|
+
* bar(X.y);
|
|
2284
|
+
* } else {
|
|
2285
|
+
* baz(X.y);
|
|
2286
|
+
* }
|
|
2287
|
+
*
|
|
2288
|
+
* Here, we could pull out X.y as a local, but if foo might modify
|
|
2289
|
+
* X.y, we have nowhere to insert the temporary. But we can rewrite
|
|
2290
|
+
* it as:
|
|
2291
|
+
*
|
|
2292
|
+
* var tmp = foo();
|
|
2293
|
+
* if (tmp) {
|
|
2294
|
+
* bar(X.y);
|
|
2295
|
+
* } else {
|
|
2296
|
+
* baz(X.y);
|
|
2297
|
+
* }
|
|
2298
|
+
*
|
|
2299
|
+
* and now we can insert a temporary before the if, but it costs
|
|
2300
|
+
* 4 bytes to do so.
|
|
2301
|
+
*
|
|
2302
|
+
* We can do the same for switch statements unless (ugh!)
|
|
2303
|
+
* the cases might modify the decl too.
|
|
2304
|
+
*
|
|
2305
|
+
* eg
|
|
2306
|
+
*
|
|
2307
|
+
* switch (foo()) {
|
|
2308
|
+
* case bar(): ...
|
|
2309
|
+
* }
|
|
2310
|
+
*
|
|
2311
|
+
*/
|
|
2312
|
+
function _isFixableStmt(node) {
|
|
2313
|
+
switch (node.type) {
|
|
2314
|
+
case "IfStatement":
|
|
2315
|
+
return 4;
|
|
2316
|
+
case "SwitchStatement":
|
|
2317
|
+
if (node.cases.every((c) => !c.test ||
|
|
2318
|
+
c.test.type === "Literal" ||
|
|
2319
|
+
c.test.type === "Identifier" ||
|
|
2320
|
+
c.test.type === "InstanceOfCase" ||
|
|
2321
|
+
(c.test.type === "UnaryExpression" && c.test.operator === ":"))) {
|
|
2322
|
+
return 4;
|
|
2323
|
+
}
|
|
2324
|
+
break;
|
|
710
2325
|
}
|
|
711
|
-
return
|
|
2326
|
+
return false;
|
|
712
2327
|
}
|
|
713
|
-
function
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
return lookupNode;
|
|
2328
|
+
function applyReplacements(func, nodeMap, declMap) {
|
|
2329
|
+
const ident = (name, node) => {
|
|
2330
|
+
return withLoc({ type: "Identifier", name }, node);
|
|
2331
|
+
};
|
|
2332
|
+
const pendingMap = new Map();
|
|
2333
|
+
const stmtStack = [func];
|
|
2334
|
+
traverseAst(func, (node) => {
|
|
2335
|
+
if (isStatement(node)) {
|
|
2336
|
+
stmtStack.push(node);
|
|
723
2337
|
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
2338
|
+
}, (node) => {
|
|
2339
|
+
const stmt = stmtStack[stmtStack.length - 1];
|
|
2340
|
+
if (stmt === node)
|
|
2341
|
+
stmtStack.pop();
|
|
2342
|
+
const events = nodeMap.get(node);
|
|
2343
|
+
if (events) {
|
|
2344
|
+
if (events.length === 1) {
|
|
2345
|
+
if (events[0].type === "ref") {
|
|
2346
|
+
if (node.type !== "Identifier" &&
|
|
2347
|
+
node.type !== "MemberExpression" &&
|
|
2348
|
+
node.type !== "Literal") {
|
|
2349
|
+
throw new Error(`Ref found, but wrong type of node: ${node.type}`);
|
|
2350
|
+
}
|
|
2351
|
+
const name = declMap.get(events[0].decl);
|
|
2352
|
+
if (!name) {
|
|
2353
|
+
throw new Error(`No replacement found for "${formatAst(node)}"`);
|
|
2354
|
+
}
|
|
2355
|
+
return ident(name, node);
|
|
2356
|
+
}
|
|
2357
|
+
else if (events[0].type === "def") {
|
|
2358
|
+
if (node.type !== "AssignmentExpression" &&
|
|
2359
|
+
node.type !== "UpdateExpression") {
|
|
2360
|
+
throw new Error(`Def found, but wrong type of node: ${node.type}`);
|
|
2361
|
+
}
|
|
2362
|
+
const target = node.type === "AssignmentExpression"
|
|
2363
|
+
? node.left
|
|
2364
|
+
: node.argument;
|
|
2365
|
+
const name = declMap.get(events[0].decl);
|
|
2366
|
+
if (!name) {
|
|
2367
|
+
throw new Error(`No replacement found for "${formatAst(target)}"`);
|
|
2368
|
+
}
|
|
2369
|
+
const id = ident(name, target);
|
|
2370
|
+
const assign = withLoc({
|
|
2371
|
+
type: "AssignmentExpression",
|
|
2372
|
+
left: target,
|
|
2373
|
+
right: { ...id },
|
|
2374
|
+
operator: "=",
|
|
2375
|
+
}, node);
|
|
2376
|
+
if (node.type === "AssignmentExpression") {
|
|
2377
|
+
node.left = id;
|
|
2378
|
+
}
|
|
2379
|
+
else {
|
|
2380
|
+
node.argument = id;
|
|
2381
|
+
}
|
|
2382
|
+
return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
events.forEach((event) => {
|
|
2386
|
+
if (event.type !== "mod") {
|
|
2387
|
+
throw new Error(`Unexpected ${event.type} found amongst multiple events`);
|
|
2388
|
+
}
|
|
2389
|
+
if (!event.decl) {
|
|
2390
|
+
throw new Error(`Unexpected null decl on mod event`);
|
|
2391
|
+
}
|
|
2392
|
+
let pending = pendingMap.get(stmt);
|
|
2393
|
+
if (!pending) {
|
|
2394
|
+
pendingMap.set(stmt, (pending = new Set()));
|
|
2395
|
+
}
|
|
2396
|
+
pending.add(event);
|
|
2397
|
+
});
|
|
749
2398
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
type: "MemberExpression",
|
|
755
|
-
object,
|
|
756
|
-
property,
|
|
757
|
-
computed: false,
|
|
758
|
-
start: node.start,
|
|
759
|
-
end: node.end,
|
|
760
|
-
loc: node.loc,
|
|
761
|
-
});
|
|
762
|
-
if (prefixes.length &&
|
|
763
|
-
prefixes[0].startsWith("$.") &&
|
|
764
|
-
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
765
|
-
const prefix = prefixes[0].split(".").slice(0, -1).reverse();
|
|
766
|
-
let found = false;
|
|
767
|
-
return prefix.reduce((current, name) => {
|
|
768
|
-
if (found)
|
|
769
|
-
return current;
|
|
770
|
-
const [, results] = state.lookup(current);
|
|
771
|
-
if (results && sameLookupResult(original, results)) {
|
|
772
|
-
found = true;
|
|
773
|
-
return current;
|
|
2399
|
+
const pending = pendingMap.get(node);
|
|
2400
|
+
if (node.type === "SequenceExpression") {
|
|
2401
|
+
if (pending) {
|
|
2402
|
+
throw new Error(`Unexpected pending list at SequenceExpression`);
|
|
774
2403
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
loc: node.loc,
|
|
781
|
-
};
|
|
782
|
-
let root = null;
|
|
783
|
-
let property = current;
|
|
784
|
-
do {
|
|
785
|
-
root = property;
|
|
786
|
-
property = property.object;
|
|
787
|
-
} while (property.type === "MemberExpression");
|
|
788
|
-
if (property.type === "ThisExpression") {
|
|
789
|
-
root.object = object;
|
|
790
|
-
return root;
|
|
2404
|
+
for (let i = node.expressions.length; i--;) {
|
|
2405
|
+
const ni = node.expressions[i];
|
|
2406
|
+
if (ni.type === "SequenceExpression") {
|
|
2407
|
+
node.expressions.splice(i, 1, ...ni.expressions);
|
|
2408
|
+
}
|
|
791
2409
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
2410
|
+
}
|
|
2411
|
+
const applyPending = (results, locNode) => {
|
|
2412
|
+
const target = results.length === 1 && results[0].type === "BlockStatement"
|
|
2413
|
+
? results[0]
|
|
2414
|
+
: null;
|
|
2415
|
+
pendingMap.delete(node);
|
|
2416
|
+
pending.forEach((event) => {
|
|
2417
|
+
const decl = event.decl;
|
|
2418
|
+
const name = declMap.get(decl);
|
|
2419
|
+
if (!name) {
|
|
2420
|
+
throw new Error(`No replacement found for "${declFullName(decl)}"`);
|
|
2421
|
+
}
|
|
2422
|
+
if (!event.id) {
|
|
2423
|
+
throw new Error(`Missing id for mod event for "${declFullName(decl)}"`);
|
|
2424
|
+
}
|
|
2425
|
+
const rhs = withLocDeep(event.id, locNode, locNode);
|
|
2426
|
+
rhs.end = rhs.start;
|
|
2427
|
+
if (rhs.loc) {
|
|
2428
|
+
rhs.loc.end = rhs.loc.start;
|
|
2429
|
+
}
|
|
2430
|
+
const insertion = withLoc({
|
|
2431
|
+
type: "ExpressionStatement",
|
|
2432
|
+
expression: withLoc({
|
|
2433
|
+
type: "AssignmentExpression",
|
|
2434
|
+
left: ident(name, rhs),
|
|
2435
|
+
right: rhs,
|
|
2436
|
+
operator: "=",
|
|
2437
|
+
}, rhs),
|
|
2438
|
+
}, rhs);
|
|
2439
|
+
if (event.type === "mod" && event.before) {
|
|
2440
|
+
if (target) {
|
|
2441
|
+
target.body.unshift(insertion);
|
|
2442
|
+
}
|
|
2443
|
+
else {
|
|
2444
|
+
results.unshift(insertion);
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
else {
|
|
2448
|
+
results.push(insertion);
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
return results.length === 1 ? null : results;
|
|
2452
|
+
};
|
|
2453
|
+
if (node.type === "ExpressionStatement" &&
|
|
2454
|
+
node.expression.type === "SequenceExpression") {
|
|
2455
|
+
const results = [];
|
|
2456
|
+
node.expression.expressions.forEach((expression) => {
|
|
2457
|
+
results.push({ ...node, expression });
|
|
2458
|
+
});
|
|
2459
|
+
if (!pending) {
|
|
2460
|
+
return results;
|
|
2461
|
+
}
|
|
2462
|
+
return applyPending(results, node);
|
|
2463
|
+
}
|
|
2464
|
+
if (pending) {
|
|
2465
|
+
return applyPending([node], node);
|
|
2466
|
+
}
|
|
2467
|
+
return null;
|
|
2468
|
+
});
|
|
803
2469
|
}
|
|
804
2470
|
|
|
805
|
-
;// CONCATENATED MODULE: external "./util.cjs"
|
|
806
|
-
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
807
2471
|
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
808
2472
|
|
|
809
2473
|
function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
@@ -906,6 +2570,7 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
906
2570
|
|
|
907
2571
|
|
|
908
2572
|
|
|
2573
|
+
|
|
909
2574
|
function collectClassInfo(state) {
|
|
910
2575
|
const toybox = state.stack[0].decls["Toybox"][0];
|
|
911
2576
|
const lang = toybox.decls["Lang"][0];
|
|
@@ -1207,7 +2872,50 @@ function getNodeValue(node) {
|
|
|
1207
2872
|
}
|
|
1208
2873
|
return [node, type];
|
|
1209
2874
|
}
|
|
1210
|
-
function
|
|
2875
|
+
function fullTypeName(state, tsp) {
|
|
2876
|
+
if (typeof tsp.name === "string") {
|
|
2877
|
+
return tsp.name;
|
|
2878
|
+
}
|
|
2879
|
+
const [, results] = state.lookupType(tsp.name);
|
|
2880
|
+
if (results && results.length === 1 && results[0].results.length === 1) {
|
|
2881
|
+
const result = results[0].results[0];
|
|
2882
|
+
if (isStateNode(result)) {
|
|
2883
|
+
return result.fullName;
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
return null;
|
|
2887
|
+
}
|
|
2888
|
+
function isBooleanExpression(state, node) {
|
|
2889
|
+
switch (node.type) {
|
|
2890
|
+
case "Literal":
|
|
2891
|
+
return typeof node.value === "boolean";
|
|
2892
|
+
case "BinaryExpression":
|
|
2893
|
+
switch (node.operator) {
|
|
2894
|
+
case "==":
|
|
2895
|
+
case "!=":
|
|
2896
|
+
case "<=":
|
|
2897
|
+
case ">=":
|
|
2898
|
+
case "<":
|
|
2899
|
+
case ">":
|
|
2900
|
+
return true;
|
|
2901
|
+
case "as":
|
|
2902
|
+
return node.right.ts.length === 1 &&
|
|
2903
|
+
node.right.ts[0].type === "TypeSpecPart" &&
|
|
2904
|
+
node.right.ts[0].name &&
|
|
2905
|
+
fullTypeName(state, node.right.ts[0]) === "$.Toybox.Lang.Boolean"
|
|
2906
|
+
? true
|
|
2907
|
+
: false;
|
|
2908
|
+
}
|
|
2909
|
+
return false;
|
|
2910
|
+
case "LogicalExpression":
|
|
2911
|
+
return (isBooleanExpression(state, node.left) &&
|
|
2912
|
+
isBooleanExpression(state, node.right));
|
|
2913
|
+
case "UnaryExpression":
|
|
2914
|
+
return node.operator === "!" && isBooleanExpression(state, node.argument);
|
|
2915
|
+
}
|
|
2916
|
+
return false;
|
|
2917
|
+
}
|
|
2918
|
+
function optimizeNode(state, node) {
|
|
1211
2919
|
switch (node.type) {
|
|
1212
2920
|
case "UnaryExpression": {
|
|
1213
2921
|
const [arg, type] = getNodeValue(node.argument);
|
|
@@ -1259,8 +2967,18 @@ function optimizeNode(node) {
|
|
|
1259
2967
|
"%": (left, right) => left % right,
|
|
1260
2968
|
"&": (left, right, type) => type === "Number" ? left & right : null,
|
|
1261
2969
|
"|": (left, right, type) => type === "Number" ? left | right : null,
|
|
2970
|
+
"^": (left, right, type) => type === "Number" ? left ^ right : null,
|
|
1262
2971
|
"<<": (left, right, type) => type === "Number" ? left << right : null,
|
|
1263
2972
|
">>": (left, right, type) => type === "Number" ? left >> right : null,
|
|
2973
|
+
"==": (left, right) => left == right,
|
|
2974
|
+
"!=": (left, right) => left != right,
|
|
2975
|
+
"<=": (left, right) => left <= right,
|
|
2976
|
+
">=": (left, right) => left >= right,
|
|
2977
|
+
"<": (left, right) => left < right,
|
|
2978
|
+
">": (left, right) => left > right,
|
|
2979
|
+
as: null,
|
|
2980
|
+
instanceof: null,
|
|
2981
|
+
has: null,
|
|
1264
2982
|
};
|
|
1265
2983
|
const op = operators[node.operator];
|
|
1266
2984
|
if (op) {
|
|
@@ -1268,11 +2986,17 @@ function optimizeNode(node) {
|
|
|
1268
2986
|
const [right, right_type] = getNodeValue(node.right);
|
|
1269
2987
|
if (!left || !right)
|
|
1270
2988
|
break;
|
|
2989
|
+
let value = null;
|
|
1271
2990
|
if (left_type != right_type ||
|
|
1272
2991
|
(left_type != "Number" && left_type != "Long")) {
|
|
1273
|
-
|
|
2992
|
+
if (node.operator !== "==" && node.operator !== "!=") {
|
|
2993
|
+
break;
|
|
2994
|
+
}
|
|
2995
|
+
value = operators[node.operator](left.value, right.value);
|
|
2996
|
+
}
|
|
2997
|
+
else {
|
|
2998
|
+
value = op(left.value, right.value, left_type);
|
|
1274
2999
|
}
|
|
1275
|
-
const value = op(left.value, right.value, left_type);
|
|
1276
3000
|
if (value === null)
|
|
1277
3001
|
break;
|
|
1278
3002
|
return {
|
|
@@ -1283,15 +3007,47 @@ function optimizeNode(node) {
|
|
|
1283
3007
|
}
|
|
1284
3008
|
break;
|
|
1285
3009
|
}
|
|
3010
|
+
case "LogicalExpression": {
|
|
3011
|
+
const [left, left_type] = getNodeValue(node.left);
|
|
3012
|
+
if (!left)
|
|
3013
|
+
break;
|
|
3014
|
+
const falsy = left.value === false ||
|
|
3015
|
+
left.value === null ||
|
|
3016
|
+
(left.value === 0 && (left_type === "Number" || left_type === "Long"));
|
|
3017
|
+
if (falsy === (node.operator === "&&")) {
|
|
3018
|
+
return left;
|
|
3019
|
+
}
|
|
3020
|
+
if (left_type !== "Boolean" &&
|
|
3021
|
+
left_type !== "Number" &&
|
|
3022
|
+
left_type !== "Long") {
|
|
3023
|
+
break;
|
|
3024
|
+
}
|
|
3025
|
+
const [right, right_type] = getNodeValue(node.right);
|
|
3026
|
+
if (right && right_type === left_type) {
|
|
3027
|
+
if (left_type === "Boolean" || node.operator === "||") {
|
|
3028
|
+
return right;
|
|
3029
|
+
}
|
|
3030
|
+
if (node.operator !== "&&") {
|
|
3031
|
+
throw new Error(`Unexpected operator "${node.operator}"`);
|
|
3032
|
+
}
|
|
3033
|
+
return { ...node, type: "BinaryExpression", operator: "&" };
|
|
3034
|
+
}
|
|
3035
|
+
if (left_type === "Boolean") {
|
|
3036
|
+
if (isBooleanExpression(state, node.right)) {
|
|
3037
|
+
return node.right;
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
break;
|
|
3041
|
+
}
|
|
1286
3042
|
case "FunctionDeclaration":
|
|
1287
|
-
if (node.body && evaluateFunction(node, null) !== false) {
|
|
3043
|
+
if (node.body && evaluateFunction(state, node, null) !== false) {
|
|
1288
3044
|
node.optimizable = true;
|
|
1289
3045
|
}
|
|
1290
3046
|
break;
|
|
1291
3047
|
}
|
|
1292
3048
|
return null;
|
|
1293
3049
|
}
|
|
1294
|
-
function evaluateFunction(func, args) {
|
|
3050
|
+
function evaluateFunction(state, func, args) {
|
|
1295
3051
|
if (!func.body || (args && args.length != func.params.length)) {
|
|
1296
3052
|
return false;
|
|
1297
3053
|
}
|
|
@@ -1330,7 +3086,7 @@ function evaluateFunction(func, args) {
|
|
|
1330
3086
|
}
|
|
1331
3087
|
// fall through;
|
|
1332
3088
|
default: {
|
|
1333
|
-
const repl = optimizeNode(node);
|
|
3089
|
+
const repl = optimizeNode(state, node);
|
|
1334
3090
|
if (repl && repl.type === "Literal")
|
|
1335
3091
|
return repl;
|
|
1336
3092
|
throw new Error("Didn't optimize");
|
|
@@ -1390,10 +3146,19 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1390
3146
|
if (!objects) {
|
|
1391
3147
|
return false;
|
|
1392
3148
|
}
|
|
1393
|
-
|
|
3149
|
+
let obj = getLiteralFromDecls(objects);
|
|
1394
3150
|
if (!obj) {
|
|
1395
3151
|
return false;
|
|
1396
3152
|
}
|
|
3153
|
+
while (obj.type === "BinaryExpression") {
|
|
3154
|
+
if (obj.left.type === "BinaryExpression" && obj.left.operator === "as") {
|
|
3155
|
+
obj = { ...obj, left: obj.left.left };
|
|
3156
|
+
}
|
|
3157
|
+
else {
|
|
3158
|
+
obj = { ...obj, left: { ...obj.left } };
|
|
3159
|
+
break;
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
1397
3162
|
inPlaceReplacement(node, obj);
|
|
1398
3163
|
return true;
|
|
1399
3164
|
};
|
|
@@ -1611,14 +3376,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1611
3376
|
return null;
|
|
1612
3377
|
};
|
|
1613
3378
|
state.post = (node) => {
|
|
1614
|
-
|
|
3379
|
+
const locals = topLocals();
|
|
3380
|
+
if (locals.node === node) {
|
|
1615
3381
|
state.localsStack.pop();
|
|
1616
3382
|
}
|
|
1617
|
-
const opt = optimizeNode(node);
|
|
3383
|
+
const opt = optimizeNode(state, node);
|
|
1618
3384
|
if (opt) {
|
|
1619
3385
|
return replace(opt, node);
|
|
1620
3386
|
}
|
|
1621
3387
|
switch (node.type) {
|
|
3388
|
+
case "BlockStatement":
|
|
3389
|
+
if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
|
|
3390
|
+
node.body.splice(0, 1, ...node.body[0].body);
|
|
3391
|
+
}
|
|
3392
|
+
break;
|
|
1622
3393
|
case "ConditionalExpression":
|
|
1623
3394
|
case "IfStatement":
|
|
1624
3395
|
if (node.test.type === "Literal" &&
|
|
@@ -1628,6 +3399,12 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1628
3399
|
return false;
|
|
1629
3400
|
return replace(rep, rep);
|
|
1630
3401
|
}
|
|
3402
|
+
else if (node.type === "IfStatement" &&
|
|
3403
|
+
node.alternate &&
|
|
3404
|
+
node.alternate.type === "BlockStatement" &&
|
|
3405
|
+
!node.alternate.body.length) {
|
|
3406
|
+
delete node.alternate;
|
|
3407
|
+
}
|
|
1631
3408
|
break;
|
|
1632
3409
|
case "WhileStatement":
|
|
1633
3410
|
if (node.test.type === "Literal" && node.test.value === false) {
|
|
@@ -1655,6 +3432,48 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1655
3432
|
return { type: "Literal", value: null, raw: "null" };
|
|
1656
3433
|
}
|
|
1657
3434
|
break;
|
|
3435
|
+
case "VariableDeclaration": {
|
|
3436
|
+
const locals = topLocals();
|
|
3437
|
+
if (locals.map &&
|
|
3438
|
+
locals.node &&
|
|
3439
|
+
locals.node.type === "BlockStatement") {
|
|
3440
|
+
let results;
|
|
3441
|
+
const declarations = node.declarations;
|
|
3442
|
+
let i = 0;
|
|
3443
|
+
let j = 0;
|
|
3444
|
+
while (i < node.declarations.length) {
|
|
3445
|
+
const decl = declarations[i++];
|
|
3446
|
+
if (decl.init && decl.init.type === "CallExpression") {
|
|
3447
|
+
const inlined = optimizeCall(state, decl.init, decl);
|
|
3448
|
+
if (!inlined)
|
|
3449
|
+
continue;
|
|
3450
|
+
if (inlined.type != "BlockStatement") {
|
|
3451
|
+
throw new Error("Unexpected inlined result");
|
|
3452
|
+
}
|
|
3453
|
+
if (!results) {
|
|
3454
|
+
results = [];
|
|
3455
|
+
}
|
|
3456
|
+
delete decl.init;
|
|
3457
|
+
results.push(withLoc({
|
|
3458
|
+
...node,
|
|
3459
|
+
declarations: declarations.slice(j, i),
|
|
3460
|
+
}, j ? declarations[j] : null, decl.id));
|
|
3461
|
+
results.push(inlined);
|
|
3462
|
+
j = i;
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
if (results) {
|
|
3466
|
+
if (j < i) {
|
|
3467
|
+
results.push({
|
|
3468
|
+
...node,
|
|
3469
|
+
declarations: declarations.slice(j, i),
|
|
3470
|
+
});
|
|
3471
|
+
}
|
|
3472
|
+
return results;
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
break;
|
|
3476
|
+
}
|
|
1658
3477
|
case "ExpressionStatement":
|
|
1659
3478
|
if (node.expression.type === "CallExpression") {
|
|
1660
3479
|
return replace(optimizeCall(state, node.expression, node), node.expression);
|
|
@@ -1699,6 +3518,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
1699
3518
|
});
|
|
1700
3519
|
delete state.pre;
|
|
1701
3520
|
delete state.post;
|
|
3521
|
+
state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
|
|
1702
3522
|
const cleanup = (node) => {
|
|
1703
3523
|
switch (node.type) {
|
|
1704
3524
|
case "ThisExpression":
|
|
@@ -1824,7 +3644,7 @@ function optimizeCall(state, node, context) {
|
|
|
1824
3644
|
callee.optimizable &&
|
|
1825
3645
|
!callee.hasOverride &&
|
|
1826
3646
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
1827
|
-
const ret = evaluateFunction(callee, node.arguments);
|
|
3647
|
+
const ret = evaluateFunction(state, callee, node.arguments);
|
|
1828
3648
|
if (ret) {
|
|
1829
3649
|
return ret;
|
|
1830
3650
|
}
|
|
@@ -1984,11 +3804,8 @@ async function api_getApiMapping(state, barrelList) {
|
|
|
1984
3804
|
return null;
|
|
1985
3805
|
}
|
|
1986
3806
|
}
|
|
1987
|
-
function api_hasProperty(obj, prop) {
|
|
1988
|
-
return obj ? Object.prototype.hasOwnProperty.call(obj, prop) : false;
|
|
1989
|
-
}
|
|
1990
3807
|
function api_isStateNode(node) {
|
|
1991
|
-
return
|
|
3808
|
+
return ast_hasProperty(node, "node");
|
|
1992
3809
|
}
|
|
1993
3810
|
function api_variableDeclarationName(node) {
|
|
1994
3811
|
return ("left" in node ? node.left : node).name;
|
|
@@ -2008,7 +3825,7 @@ function checkOne(state, ns, decls, node, isStatic) {
|
|
|
2008
3825
|
}
|
|
2009
3826
|
return cls.superClass.reduce((result, sup) => {
|
|
2010
3827
|
const sdecls = sup[decls];
|
|
2011
|
-
const next =
|
|
3828
|
+
const next = ast_hasProperty(sdecls, node.name)
|
|
2012
3829
|
? sdecls[node.name]
|
|
2013
3830
|
: superChain(sup);
|
|
2014
3831
|
return next ? (result ? result.concat(next) : next) : result;
|
|
@@ -2033,7 +3850,7 @@ function checkOne(state, ns, decls, node, isStatic) {
|
|
|
2033
3850
|
};
|
|
2034
3851
|
if (api_isStateNode(ns)) {
|
|
2035
3852
|
const ndecls = ns[decls];
|
|
2036
|
-
if (
|
|
3853
|
+
if (ast_hasProperty(ndecls, node.name)) {
|
|
2037
3854
|
return ndecls[node.name];
|
|
2038
3855
|
}
|
|
2039
3856
|
switch (ns.type) {
|
|
@@ -2313,7 +4130,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2313
4130
|
if (name) {
|
|
2314
4131
|
if (!parent.decls)
|
|
2315
4132
|
parent.decls = {};
|
|
2316
|
-
if (
|
|
4133
|
+
if (ast_hasProperty(parent.decls, name)) {
|
|
2317
4134
|
const what = node.type == "ModuleDeclaration" ? "type" : "node";
|
|
2318
4135
|
const e = parent.decls[name].find((d) => api_isStateNode(d) && d[what] == elm[what]);
|
|
2319
4136
|
if (e != null) {
|
|
@@ -2339,7 +4156,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2339
4156
|
elm.decls = { [name]: [elm] };
|
|
2340
4157
|
if (!parent.type_decls)
|
|
2341
4158
|
parent.type_decls = {};
|
|
2342
|
-
if (!
|
|
4159
|
+
if (!ast_hasProperty(parent.type_decls, name)) {
|
|
2343
4160
|
parent.type_decls[name] = [];
|
|
2344
4161
|
}
|
|
2345
4162
|
parent.type_decls[name].push(elm);
|
|
@@ -2363,7 +4180,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2363
4180
|
const [parent] = state.stack.slice(-1);
|
|
2364
4181
|
if (!parent.type_decls)
|
|
2365
4182
|
parent.type_decls = {};
|
|
2366
|
-
if (!
|
|
4183
|
+
if (!ast_hasProperty(parent.type_decls, name)) {
|
|
2367
4184
|
parent.type_decls[name] = [];
|
|
2368
4185
|
}
|
|
2369
4186
|
else if (parent.type_decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == node)) {
|
|
@@ -2387,7 +4204,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2387
4204
|
const stack = state.stackClone();
|
|
2388
4205
|
node.declarations.forEach((decl) => {
|
|
2389
4206
|
const name = api_variableDeclarationName(decl.id);
|
|
2390
|
-
if (!
|
|
4207
|
+
if (!ast_hasProperty(decls, name)) {
|
|
2391
4208
|
decls[name] = [];
|
|
2392
4209
|
}
|
|
2393
4210
|
else if (decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == decl)) {
|
|
@@ -2402,7 +4219,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2402
4219
|
stack,
|
|
2403
4220
|
});
|
|
2404
4221
|
if (node.kind == "const") {
|
|
2405
|
-
if (!
|
|
4222
|
+
if (!ast_hasProperty(state.index, name)) {
|
|
2406
4223
|
state.index[name] = [];
|
|
2407
4224
|
}
|
|
2408
4225
|
(0,external_util_cjs_namespaceObject.pushUnique)(state.index[name], parent);
|
|
@@ -2448,11 +4265,11 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
2448
4265
|
prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.test(init.raw)) {
|
|
2449
4266
|
prev = init.value;
|
|
2450
4267
|
}
|
|
2451
|
-
if (!
|
|
4268
|
+
if (!ast_hasProperty(values, name)) {
|
|
2452
4269
|
values[name] = [];
|
|
2453
4270
|
}
|
|
2454
4271
|
(0,external_util_cjs_namespaceObject.pushUnique)(values[name], m);
|
|
2455
|
-
if (!
|
|
4272
|
+
if (!ast_hasProperty(state.index, name)) {
|
|
2456
4273
|
state.index[name] = [];
|
|
2457
4274
|
}
|
|
2458
4275
|
(0,external_util_cjs_namespaceObject.pushUnique)(state.index[name], parent);
|
|
@@ -2592,7 +4409,7 @@ function findUsing(state, stack, using) {
|
|
|
2592
4409
|
find(node.object);
|
|
2593
4410
|
name = node.property.name;
|
|
2594
4411
|
}
|
|
2595
|
-
if (
|
|
4412
|
+
if (ast_hasProperty(module.decls, name)) {
|
|
2596
4413
|
const decls = module.decls[name];
|
|
2597
4414
|
if (decls &&
|
|
2598
4415
|
decls.length === 1 &&
|
|
@@ -2615,7 +4432,7 @@ function findUsing(state, stack, using) {
|
|
|
2615
4432
|
function findUsingForNode(state, stack, i, node, isType) {
|
|
2616
4433
|
while (i >= 0) {
|
|
2617
4434
|
const si = stack[i--];
|
|
2618
|
-
if (
|
|
4435
|
+
if (ast_hasProperty(si.usings, node.name)) {
|
|
2619
4436
|
const using = si.usings[node.name];
|
|
2620
4437
|
const module = findUsing(state, stack, using);
|
|
2621
4438
|
return module && [module];
|
|
@@ -2625,7 +4442,7 @@ function findUsingForNode(state, stack, i, node, isType) {
|
|
|
2625
4442
|
const using = si.imports[j];
|
|
2626
4443
|
const module = findUsing(state, stack, using);
|
|
2627
4444
|
if (module) {
|
|
2628
|
-
if (
|
|
4445
|
+
if (ast_hasProperty(module.type_decls, node.name)) {
|
|
2629
4446
|
return module.type_decls[node.name];
|
|
2630
4447
|
}
|
|
2631
4448
|
}
|
|
@@ -2635,6 +4452,8 @@ function findUsingForNode(state, stack, i, node, isType) {
|
|
|
2635
4452
|
return null;
|
|
2636
4453
|
}
|
|
2637
4454
|
|
|
4455
|
+
})();
|
|
4456
|
+
|
|
2638
4457
|
var __webpack_export_target__ = exports;
|
|
2639
4458
|
for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i];
|
|
2640
4459
|
if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });
|