@markw65/monkeyc-optimizer 1.0.27 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/build/api.cjs +2087 -226
- package/build/optimizer.cjs +2010 -172
- package/build/src/api.d.ts +2 -4
- package/build/src/ast.d.ts +7 -0
- package/build/src/control-flow.d.ts +20 -0
- package/build/src/inliner.d.ts +3 -3
- package/build/src/mc-rewrite.d.ts +1 -1
- package/build/src/optimizer.d.ts +1 -0
- package/package.json +4 -2
package/build/optimizer.cjs
CHANGED
|
@@ -2172,6 +2172,185 @@ function pendGo(self, fn) {
|
|
|
2172
2172
|
}
|
|
2173
2173
|
|
|
2174
2174
|
|
|
2175
|
+
/***/ }),
|
|
2176
|
+
|
|
2177
|
+
/***/ 2789:
|
|
2178
|
+
/***/ ((module) => {
|
|
2179
|
+
|
|
2180
|
+
/**
|
|
2181
|
+
* Expose `PriorityQueue`.
|
|
2182
|
+
*/
|
|
2183
|
+
module.exports = PriorityQueue;
|
|
2184
|
+
|
|
2185
|
+
/**
|
|
2186
|
+
* Initializes a new empty `PriorityQueue` with the given `comparator(a, b)`
|
|
2187
|
+
* function, uses `.DEFAULT_COMPARATOR()` when no function is provided.
|
|
2188
|
+
*
|
|
2189
|
+
* The comparator function must return a positive number when `a > b`, 0 when
|
|
2190
|
+
* `a == b` and a negative number when `a < b`.
|
|
2191
|
+
*
|
|
2192
|
+
* @param {Function}
|
|
2193
|
+
* @return {PriorityQueue}
|
|
2194
|
+
* @api public
|
|
2195
|
+
*/
|
|
2196
|
+
function PriorityQueue(comparator) {
|
|
2197
|
+
this._comparator = comparator || PriorityQueue.DEFAULT_COMPARATOR;
|
|
2198
|
+
this._elements = [];
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
/**
|
|
2202
|
+
* Compares `a` and `b`, when `a > b` it returns a positive number, when
|
|
2203
|
+
* it returns 0 and when `a < b` it returns a negative number.
|
|
2204
|
+
*
|
|
2205
|
+
* @param {String|Number} a
|
|
2206
|
+
* @param {String|Number} b
|
|
2207
|
+
* @return {Number}
|
|
2208
|
+
* @api public
|
|
2209
|
+
*/
|
|
2210
|
+
PriorityQueue.DEFAULT_COMPARATOR = function(a, b) {
|
|
2211
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
2212
|
+
return a - b;
|
|
2213
|
+
} else {
|
|
2214
|
+
a = a.toString();
|
|
2215
|
+
b = b.toString();
|
|
2216
|
+
|
|
2217
|
+
if (a == b) return 0;
|
|
2218
|
+
|
|
2219
|
+
return (a > b) ? 1 : -1;
|
|
2220
|
+
}
|
|
2221
|
+
};
|
|
2222
|
+
|
|
2223
|
+
/**
|
|
2224
|
+
* Returns whether the priority queue is empty or not.
|
|
2225
|
+
*
|
|
2226
|
+
* @return {Boolean}
|
|
2227
|
+
* @api public
|
|
2228
|
+
*/
|
|
2229
|
+
PriorityQueue.prototype.isEmpty = function() {
|
|
2230
|
+
return this.size() === 0;
|
|
2231
|
+
};
|
|
2232
|
+
|
|
2233
|
+
/**
|
|
2234
|
+
* Peeks at the top element of the priority queue.
|
|
2235
|
+
*
|
|
2236
|
+
* @return {Object}
|
|
2237
|
+
* @throws {Error} when the queue is empty.
|
|
2238
|
+
* @api public
|
|
2239
|
+
*/
|
|
2240
|
+
PriorityQueue.prototype.peek = function() {
|
|
2241
|
+
if (this.isEmpty()) throw new Error('PriorityQueue is empty');
|
|
2242
|
+
|
|
2243
|
+
return this._elements[0];
|
|
2244
|
+
};
|
|
2245
|
+
|
|
2246
|
+
/**
|
|
2247
|
+
* Dequeues the top element of the priority queue.
|
|
2248
|
+
*
|
|
2249
|
+
* @return {Object}
|
|
2250
|
+
* @throws {Error} when the queue is empty.
|
|
2251
|
+
* @api public
|
|
2252
|
+
*/
|
|
2253
|
+
PriorityQueue.prototype.deq = function() {
|
|
2254
|
+
var first = this.peek();
|
|
2255
|
+
var last = this._elements.pop();
|
|
2256
|
+
var size = this.size();
|
|
2257
|
+
|
|
2258
|
+
if (size === 0) return first;
|
|
2259
|
+
|
|
2260
|
+
this._elements[0] = last;
|
|
2261
|
+
var current = 0;
|
|
2262
|
+
|
|
2263
|
+
while (current < size) {
|
|
2264
|
+
var largest = current;
|
|
2265
|
+
var left = (2 * current) + 1;
|
|
2266
|
+
var right = (2 * current) + 2;
|
|
2267
|
+
|
|
2268
|
+
if (left < size && this._compare(left, largest) >= 0) {
|
|
2269
|
+
largest = left;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
if (right < size && this._compare(right, largest) >= 0) {
|
|
2273
|
+
largest = right;
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
if (largest === current) break;
|
|
2277
|
+
|
|
2278
|
+
this._swap(largest, current);
|
|
2279
|
+
current = largest;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
return first;
|
|
2283
|
+
};
|
|
2284
|
+
|
|
2285
|
+
/**
|
|
2286
|
+
* Enqueues the `element` at the priority queue and returns its new size.
|
|
2287
|
+
*
|
|
2288
|
+
* @param {Object} element
|
|
2289
|
+
* @return {Number}
|
|
2290
|
+
* @api public
|
|
2291
|
+
*/
|
|
2292
|
+
PriorityQueue.prototype.enq = function(element) {
|
|
2293
|
+
var size = this._elements.push(element);
|
|
2294
|
+
var current = size - 1;
|
|
2295
|
+
|
|
2296
|
+
while (current > 0) {
|
|
2297
|
+
var parent = Math.floor((current - 1) / 2);
|
|
2298
|
+
|
|
2299
|
+
if (this._compare(current, parent) <= 0) break;
|
|
2300
|
+
|
|
2301
|
+
this._swap(parent, current);
|
|
2302
|
+
current = parent;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
return size;
|
|
2306
|
+
};
|
|
2307
|
+
|
|
2308
|
+
/**
|
|
2309
|
+
* Returns the size of the priority queue.
|
|
2310
|
+
*
|
|
2311
|
+
* @return {Number}
|
|
2312
|
+
* @api public
|
|
2313
|
+
*/
|
|
2314
|
+
PriorityQueue.prototype.size = function() {
|
|
2315
|
+
return this._elements.length;
|
|
2316
|
+
};
|
|
2317
|
+
|
|
2318
|
+
/**
|
|
2319
|
+
* Iterates over queue elements
|
|
2320
|
+
*
|
|
2321
|
+
* @param {Function} fn
|
|
2322
|
+
*/
|
|
2323
|
+
PriorityQueue.prototype.forEach = function(fn) {
|
|
2324
|
+
return this._elements.forEach(fn);
|
|
2325
|
+
};
|
|
2326
|
+
|
|
2327
|
+
/**
|
|
2328
|
+
* Compares the values at position `a` and `b` in the priority queue using its
|
|
2329
|
+
* comparator function.
|
|
2330
|
+
*
|
|
2331
|
+
* @param {Number} a
|
|
2332
|
+
* @param {Number} b
|
|
2333
|
+
* @return {Number}
|
|
2334
|
+
* @api private
|
|
2335
|
+
*/
|
|
2336
|
+
PriorityQueue.prototype._compare = function(a, b) {
|
|
2337
|
+
return this._comparator(this._elements[a], this._elements[b]);
|
|
2338
|
+
};
|
|
2339
|
+
|
|
2340
|
+
/**
|
|
2341
|
+
* Swaps the values at position `a` and `b` in the priority queue.
|
|
2342
|
+
*
|
|
2343
|
+
* @param {Number} a
|
|
2344
|
+
* @param {Number} b
|
|
2345
|
+
* @api private
|
|
2346
|
+
*/
|
|
2347
|
+
PriorityQueue.prototype._swap = function(a, b) {
|
|
2348
|
+
var aux = this._elements[a];
|
|
2349
|
+
this._elements[a] = this._elements[b];
|
|
2350
|
+
this._elements[b] = aux;
|
|
2351
|
+
};
|
|
2352
|
+
|
|
2353
|
+
|
|
2175
2354
|
/***/ }),
|
|
2176
2355
|
|
|
2177
2356
|
/***/ 4286:
|
|
@@ -9874,17 +10053,17 @@ async function build_project(product, options, lineCallback) {
|
|
|
9874
10053
|
if (!product) {
|
|
9875
10054
|
extraArgs.push("-e");
|
|
9876
10055
|
}
|
|
9877
|
-
switch (typeCheckLevel) {
|
|
9878
|
-
case "
|
|
10056
|
+
switch (typeCheckLevel?.toLowerCase()) {
|
|
10057
|
+
case "off":
|
|
9879
10058
|
extraArgs.push("-l", "0");
|
|
9880
10059
|
break;
|
|
9881
|
-
case "
|
|
10060
|
+
case "gradual":
|
|
9882
10061
|
extraArgs.push("-l", "1");
|
|
9883
10062
|
break;
|
|
9884
|
-
case "
|
|
10063
|
+
case "informative":
|
|
9885
10064
|
extraArgs.push("-l", "2");
|
|
9886
10065
|
break;
|
|
9887
|
-
case "
|
|
10066
|
+
case "strict":
|
|
9888
10067
|
extraArgs.push("-l", "3");
|
|
9889
10068
|
break;
|
|
9890
10069
|
}
|
|
@@ -10812,62 +10991,68 @@ function _check(x) {
|
|
|
10812
10991
|
x = y;
|
|
10813
10992
|
}
|
|
10814
10993
|
const mctreeTypeInfo = {
|
|
10815
|
-
ArrayExpression: ["elements"],
|
|
10816
|
-
AssignmentExpression: ["left", "right"],
|
|
10817
|
-
AttributeList: ["attributes"],
|
|
10818
|
-
Attributes: ["elements"],
|
|
10819
|
-
BinaryExpression: ["left", "right"],
|
|
10820
|
-
Block: [],
|
|
10821
|
-
BlockStatement: ["body", "innerComments"],
|
|
10822
|
-
BreakStatement: [],
|
|
10823
|
-
CallExpression: ["callee", "arguments"],
|
|
10824
|
-
CatchClause: ["param", "body"],
|
|
10825
|
-
CatchClauses: ["catches"],
|
|
10826
|
-
ClassBody: ["body"],
|
|
10827
|
-
ClassDeclaration: ["attrs", "id", "superClass", "body"],
|
|
10828
|
-
ClassElement: ["item"],
|
|
10829
|
-
ConditionalExpression:
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10864
|
-
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10994
|
+
ArrayExpression: { keys: ["elements"], expr: true },
|
|
10995
|
+
AssignmentExpression: { keys: ["left", "right"], expr: true },
|
|
10996
|
+
AttributeList: { keys: ["attributes"] },
|
|
10997
|
+
Attributes: { keys: ["elements"] },
|
|
10998
|
+
BinaryExpression: { keys: ["left", "right"], expr: true },
|
|
10999
|
+
Block: { keys: [] },
|
|
11000
|
+
BlockStatement: { keys: ["body", "innerComments"], stmt: true },
|
|
11001
|
+
BreakStatement: { keys: [], stmt: true },
|
|
11002
|
+
CallExpression: { keys: ["callee", "arguments"], expr: true },
|
|
11003
|
+
CatchClause: { keys: ["param", "body"] },
|
|
11004
|
+
CatchClauses: { keys: ["catches"] },
|
|
11005
|
+
ClassBody: { keys: ["body"] },
|
|
11006
|
+
ClassDeclaration: { keys: ["attrs", "id", "superClass", "body"], stmt: true },
|
|
11007
|
+
ClassElement: { keys: ["item"] },
|
|
11008
|
+
ConditionalExpression: {
|
|
11009
|
+
keys: ["test", "consequent", "alternate"],
|
|
11010
|
+
expr: true,
|
|
11011
|
+
},
|
|
11012
|
+
ContinueStatement: { keys: [], stmt: true },
|
|
11013
|
+
DoWhileStatement: { keys: ["body", "test"], stmt: true },
|
|
11014
|
+
EnumDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
|
|
11015
|
+
EnumStringBody: { keys: ["members"] },
|
|
11016
|
+
EnumStringMember: { keys: ["id", "init"] },
|
|
11017
|
+
ExpressionStatement: { keys: ["expression"], stmt: true },
|
|
11018
|
+
ForStatement: { keys: ["init", "test", "body", "update"], stmt: true },
|
|
11019
|
+
FunctionDeclaration: {
|
|
11020
|
+
keys: ["attrs", "id", "params", "returnType", "body"],
|
|
11021
|
+
stmt: true,
|
|
11022
|
+
},
|
|
11023
|
+
Identifier: { keys: [], expr: true },
|
|
11024
|
+
IfStatement: { keys: ["test", "consequent", "alternate"], stmt: true },
|
|
11025
|
+
ImportModule: { keys: ["id"] },
|
|
11026
|
+
InstanceOfCase: { keys: ["id"] },
|
|
11027
|
+
Line: { keys: [] },
|
|
11028
|
+
Literal: { keys: [], expr: true },
|
|
11029
|
+
LogicalExpression: { keys: ["left", "right"], expr: true },
|
|
11030
|
+
MemberExpression: { keys: ["object", "property"], expr: true },
|
|
11031
|
+
MethodDefinition: { keys: ["params", "returnType"] },
|
|
11032
|
+
ModuleDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
|
|
11033
|
+
MultiLine: { keys: [] },
|
|
11034
|
+
NewExpression: { keys: ["callee", "arguments"], expr: true },
|
|
11035
|
+
ObjectExpression: { keys: ["properties"], expr: true },
|
|
11036
|
+
ParenthesizedExpression: { keys: ["expression"], expr: true },
|
|
11037
|
+
Program: { keys: ["body", "comments"] },
|
|
11038
|
+
Property: { keys: ["key", "value"] },
|
|
11039
|
+
ReturnStatement: { keys: ["argument"], stmt: true },
|
|
11040
|
+
SequenceExpression: { keys: ["expressions"], expr: true },
|
|
11041
|
+
SizedArrayExpression: { keys: ["size", "ts"], expr: true },
|
|
11042
|
+
SwitchCase: { keys: ["test", "consequent"] },
|
|
11043
|
+
SwitchStatement: { keys: ["discriminant", "cases"], stmt: true },
|
|
11044
|
+
ThisExpression: { keys: [], expr: true },
|
|
11045
|
+
ThrowStatement: { keys: ["argument"], stmt: true },
|
|
11046
|
+
TryStatement: { keys: ["block", "handler", "finalizer"], stmt: true },
|
|
11047
|
+
TypedefDeclaration: { keys: ["attrs", "id", "ts"], stmt: true },
|
|
11048
|
+
TypeSpecList: { keys: ["ts"] },
|
|
11049
|
+
TypeSpecPart: { keys: ["name", "body", "callspec", "generics"] },
|
|
11050
|
+
UnaryExpression: { keys: ["argument"], expr: true },
|
|
11051
|
+
UpdateExpression: { keys: ["argument"], expr: true },
|
|
11052
|
+
Using: { keys: ["id", "as"] },
|
|
11053
|
+
VariableDeclaration: { keys: ["attrs", "declarations"], stmt: true },
|
|
11054
|
+
VariableDeclarator: { keys: ["id", "init"] },
|
|
11055
|
+
WhileStatement: { keys: ["test", "body"], stmt: true },
|
|
10871
11056
|
};
|
|
10872
11057
|
function isMCTreeNode(node) {
|
|
10873
11058
|
return node ? typeof node === "object" && "type" in node : false;
|
|
@@ -10892,7 +11077,7 @@ function traverseAst(node, pre, post) {
|
|
|
10892
11077
|
if (!mctreeTypeInfo[node.type]) {
|
|
10893
11078
|
throw new Error("what?");
|
|
10894
11079
|
}
|
|
10895
|
-
for (const key of nodes || mctreeTypeInfo[node.type]) {
|
|
11080
|
+
for (const key of nodes || mctreeTypeInfo[node.type].keys) {
|
|
10896
11081
|
const value = node[key];
|
|
10897
11082
|
if (!value)
|
|
10898
11083
|
continue;
|
|
@@ -10919,13 +11104,21 @@ function traverseAst(node, pre, post) {
|
|
|
10919
11104
|
}
|
|
10920
11105
|
}
|
|
10921
11106
|
else if (isMCTreeNode(value)) {
|
|
10922
|
-
|
|
11107
|
+
let repl = traverseAst(value, pre, post);
|
|
10923
11108
|
if (repl === false) {
|
|
10924
11109
|
delete node[key];
|
|
10925
11110
|
}
|
|
10926
11111
|
else if (repl != null) {
|
|
10927
11112
|
if (Array.isArray(repl)) {
|
|
10928
|
-
|
|
11113
|
+
if (isStatement(value) && repl.every((s) => isStatement(s))) {
|
|
11114
|
+
repl = withLoc({
|
|
11115
|
+
type: "BlockStatement",
|
|
11116
|
+
body: repl,
|
|
11117
|
+
}, repl[0], repl[repl.length - 1]);
|
|
11118
|
+
}
|
|
11119
|
+
else {
|
|
11120
|
+
throw new Error("Array returned by traverseAst in Node context");
|
|
11121
|
+
}
|
|
10929
11122
|
}
|
|
10930
11123
|
node[key] = repl;
|
|
10931
11124
|
}
|
|
@@ -10933,6 +11126,55 @@ function traverseAst(node, pre, post) {
|
|
|
10933
11126
|
}
|
|
10934
11127
|
return post && post(node);
|
|
10935
11128
|
}
|
|
11129
|
+
function isStatement(node) {
|
|
11130
|
+
return hasProperty(mctreeTypeInfo[node.type], "stmt");
|
|
11131
|
+
}
|
|
11132
|
+
function isExpression(node) {
|
|
11133
|
+
return hasProperty(mctreeTypeInfo[node.type], "expr");
|
|
11134
|
+
}
|
|
11135
|
+
function mayThrow(node) {
|
|
11136
|
+
switch (node.type) {
|
|
11137
|
+
case "BinaryExpression":
|
|
11138
|
+
case "CallExpression":
|
|
11139
|
+
case "ConditionalExpression":
|
|
11140
|
+
case "LogicalExpression":
|
|
11141
|
+
case "NewExpression":
|
|
11142
|
+
case "ThrowStatement":
|
|
11143
|
+
case "UnaryExpression":
|
|
11144
|
+
case "UpdateExpression":
|
|
11145
|
+
return true;
|
|
11146
|
+
default:
|
|
11147
|
+
return false;
|
|
11148
|
+
}
|
|
11149
|
+
}
|
|
11150
|
+
function hasProperty(obj, prop) {
|
|
11151
|
+
return obj ? Object.prototype.hasOwnProperty.call(obj, prop) : false;
|
|
11152
|
+
}
|
|
11153
|
+
function withLoc(node, start, end) {
|
|
11154
|
+
if (start && start.loc) {
|
|
11155
|
+
node.start = start.start;
|
|
11156
|
+
if (!node.end)
|
|
11157
|
+
node.end = start.end;
|
|
11158
|
+
node.loc = { ...(node.loc || start.loc), start: start.loc.start };
|
|
11159
|
+
}
|
|
11160
|
+
if (end && end.loc) {
|
|
11161
|
+
node.end = end.end;
|
|
11162
|
+
node.loc = { ...(node.loc || end.loc), end: end.loc.end };
|
|
11163
|
+
}
|
|
11164
|
+
return node;
|
|
11165
|
+
}
|
|
11166
|
+
function withLocDeep(node, start, end) {
|
|
11167
|
+
node = withLoc({ ...node }, start, end);
|
|
11168
|
+
for (const key of mctreeTypeInfo[node.type].keys) {
|
|
11169
|
+
const value = node[key];
|
|
11170
|
+
if (!value)
|
|
11171
|
+
continue;
|
|
11172
|
+
const fix = (v) => isMCTreeNode(v) ? withLocDeep(v, start, end) : v;
|
|
11173
|
+
const repl = Array.isArray(value) ? value.map(fix) : fix(value);
|
|
11174
|
+
node[key] = repl;
|
|
11175
|
+
}
|
|
11176
|
+
return node;
|
|
11177
|
+
}
|
|
10936
11178
|
|
|
10937
11179
|
;// CONCATENATED MODULE: ./src/variable-renamer.ts
|
|
10938
11180
|
|
|
@@ -11187,6 +11429,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
11187
11429
|
const post = state.post;
|
|
11188
11430
|
state.inlining = true;
|
|
11189
11431
|
let insertedVariableDecls = null;
|
|
11432
|
+
const replacements = new Set();
|
|
11190
11433
|
try {
|
|
11191
11434
|
state.pre = (node) => {
|
|
11192
11435
|
if (failed)
|
|
@@ -11194,7 +11437,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
11194
11437
|
node.start = call.start;
|
|
11195
11438
|
node.end = call.end;
|
|
11196
11439
|
node.loc = call.loc;
|
|
11197
|
-
if (node
|
|
11440
|
+
if (replacements.has(node))
|
|
11198
11441
|
return false;
|
|
11199
11442
|
const result = pre(node, state);
|
|
11200
11443
|
if (!insertedVariableDecls && node.type === "BlockStatement") {
|
|
@@ -11222,6 +11465,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
11222
11465
|
kind: "var",
|
|
11223
11466
|
};
|
|
11224
11467
|
node.body.unshift(insertedVariableDecls);
|
|
11468
|
+
replacements.add(insertedVariableDecls);
|
|
11225
11469
|
}
|
|
11226
11470
|
return result;
|
|
11227
11471
|
};
|
|
@@ -11236,7 +11480,9 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
11236
11480
|
if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
|
|
11237
11481
|
const ix = params[node.name];
|
|
11238
11482
|
if (ix >= 0) {
|
|
11239
|
-
replacement = call.arguments[ix];
|
|
11483
|
+
replacement = { ...call.arguments[ix] };
|
|
11484
|
+
replacements.add(replacement);
|
|
11485
|
+
return replacement;
|
|
11240
11486
|
}
|
|
11241
11487
|
break;
|
|
11242
11488
|
}
|
|
@@ -11273,6 +11519,10 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
11273
11519
|
}
|
|
11274
11520
|
}
|
|
11275
11521
|
function unused(expression, top) {
|
|
11522
|
+
const estmt = (expression) => withLoc({
|
|
11523
|
+
type: "ExpressionStatement",
|
|
11524
|
+
expression,
|
|
11525
|
+
}, expression);
|
|
11276
11526
|
switch (expression.type) {
|
|
11277
11527
|
case "Literal":
|
|
11278
11528
|
return [];
|
|
@@ -11284,9 +11534,50 @@ function unused(expression, top) {
|
|
|
11284
11534
|
if (expression.operator === "as") {
|
|
11285
11535
|
return unused(expression.left);
|
|
11286
11536
|
}
|
|
11287
|
-
// fall through
|
|
11288
|
-
case "LogicalExpression":
|
|
11289
11537
|
return unused(expression.left).concat(unused(expression.right));
|
|
11538
|
+
case "LogicalExpression": {
|
|
11539
|
+
const right = unused(expression.right);
|
|
11540
|
+
if (!right.length)
|
|
11541
|
+
return unused(expression.left);
|
|
11542
|
+
const consequent = withLoc({
|
|
11543
|
+
type: "BlockStatement",
|
|
11544
|
+
body: [estmt(expression.right)],
|
|
11545
|
+
}, expression.right);
|
|
11546
|
+
let alternate;
|
|
11547
|
+
if (expression.operator == "||") {
|
|
11548
|
+
alternate = { ...consequent };
|
|
11549
|
+
consequent.body = [];
|
|
11550
|
+
}
|
|
11551
|
+
return [
|
|
11552
|
+
withLoc({
|
|
11553
|
+
type: "IfStatement",
|
|
11554
|
+
test: expression.left,
|
|
11555
|
+
consequent,
|
|
11556
|
+
alternate,
|
|
11557
|
+
}, expression),
|
|
11558
|
+
];
|
|
11559
|
+
}
|
|
11560
|
+
case "ConditionalExpression": {
|
|
11561
|
+
const consequentExprs = unused(expression.consequent);
|
|
11562
|
+
const alternateExprs = unused(expression.alternate);
|
|
11563
|
+
if (!consequentExprs.length && !alternateExprs.length) {
|
|
11564
|
+
return unused(expression.test);
|
|
11565
|
+
}
|
|
11566
|
+
return [
|
|
11567
|
+
withLoc({
|
|
11568
|
+
type: "IfStatement",
|
|
11569
|
+
test: expression.test,
|
|
11570
|
+
consequent: withLoc({
|
|
11571
|
+
type: "BlockStatement",
|
|
11572
|
+
body: consequentExprs,
|
|
11573
|
+
}, expression.consequent),
|
|
11574
|
+
alternate: withLoc({
|
|
11575
|
+
type: "BlockStatement",
|
|
11576
|
+
body: alternateExprs,
|
|
11577
|
+
}, expression.alternate),
|
|
11578
|
+
}, expression),
|
|
11579
|
+
];
|
|
11580
|
+
}
|
|
11290
11581
|
case "UnaryExpression":
|
|
11291
11582
|
return unused(expression.argument);
|
|
11292
11583
|
case "MemberExpression":
|
|
@@ -11301,17 +11592,7 @@ function unused(expression, top) {
|
|
|
11301
11592
|
.map((p) => unused(p.key).concat(unused(p.value)))
|
|
11302
11593
|
.flat(1);
|
|
11303
11594
|
}
|
|
11304
|
-
return top
|
|
11305
|
-
? null
|
|
11306
|
-
: [
|
|
11307
|
-
{
|
|
11308
|
-
type: "ExpressionStatement",
|
|
11309
|
-
expression,
|
|
11310
|
-
start: expression.start,
|
|
11311
|
-
end: expression.end,
|
|
11312
|
-
loc: expression.loc,
|
|
11313
|
-
},
|
|
11314
|
-
];
|
|
11595
|
+
return top ? null : [estmt(expression)];
|
|
11315
11596
|
}
|
|
11316
11597
|
function diagnostic(state, loc, message, type = "INFO") {
|
|
11317
11598
|
if (!loc || !loc.source)
|
|
@@ -11344,6 +11625,10 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
11344
11625
|
if (!func.node || !func.node.body) {
|
|
11345
11626
|
return null;
|
|
11346
11627
|
}
|
|
11628
|
+
const lastStmt = (block) => {
|
|
11629
|
+
const last = block.body.slice(-1)[0];
|
|
11630
|
+
return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
|
|
11631
|
+
};
|
|
11347
11632
|
let retStmtCount = 0;
|
|
11348
11633
|
if (context.type === "ReturnStatement") {
|
|
11349
11634
|
const last = func.node.body.body.slice(-1)[0];
|
|
@@ -11359,15 +11644,19 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
11359
11644
|
if (retStmtCount > 1) {
|
|
11360
11645
|
inlineDiagnostic(state, func, call, "Function had more than one return statement");
|
|
11361
11646
|
}
|
|
11362
|
-
else if (context.type === "AssignmentExpression"
|
|
11647
|
+
else if ((context.type === "AssignmentExpression" ||
|
|
11648
|
+
context.type === "VariableDeclarator") &&
|
|
11649
|
+
retStmtCount !== 1) {
|
|
11363
11650
|
inlineDiagnostic(state, func, call, "Function did not have a return statement");
|
|
11364
11651
|
return null;
|
|
11365
11652
|
}
|
|
11366
11653
|
if (retStmtCount === 1) {
|
|
11367
|
-
const last = func.node.body
|
|
11654
|
+
const [last] = lastStmt(func.node.body);
|
|
11368
11655
|
if (!last ||
|
|
11369
11656
|
last.type !== "ReturnStatement" ||
|
|
11370
|
-
(context.type === "AssignmentExpression"
|
|
11657
|
+
((context.type === "AssignmentExpression" ||
|
|
11658
|
+
context.type === "VariableDeclarator") &&
|
|
11659
|
+
!last.argument)) {
|
|
11371
11660
|
inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
|
|
11372
11661
|
return null;
|
|
11373
11662
|
}
|
|
@@ -11387,23 +11676,39 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
11387
11676
|
}
|
|
11388
11677
|
diagnostic(state, call.loc, null);
|
|
11389
11678
|
if (context.type !== "ReturnStatement" && retStmtCount) {
|
|
11390
|
-
const last = body
|
|
11679
|
+
const [last, block] = lastStmt(body);
|
|
11391
11680
|
if (last.type != "ReturnStatement") {
|
|
11392
11681
|
throw new Error("ReturnStatement got lost!");
|
|
11393
11682
|
}
|
|
11394
|
-
if (
|
|
11395
|
-
context.
|
|
11396
|
-
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
-
|
|
11403
|
-
|
|
11683
|
+
if (last.argument) {
|
|
11684
|
+
if (context.type === "AssignmentExpression") {
|
|
11685
|
+
context.right = last.argument;
|
|
11686
|
+
block.body[block.body.length - 1] = {
|
|
11687
|
+
type: "ExpressionStatement",
|
|
11688
|
+
expression: context,
|
|
11689
|
+
};
|
|
11690
|
+
}
|
|
11691
|
+
else if (context.type === "VariableDeclarator") {
|
|
11692
|
+
const { id, init: _init, kind: _kind, ...rest } = context;
|
|
11693
|
+
block.body[block.body.length - 1] = {
|
|
11694
|
+
...rest,
|
|
11695
|
+
type: "ExpressionStatement",
|
|
11696
|
+
expression: {
|
|
11697
|
+
...rest,
|
|
11698
|
+
type: "AssignmentExpression",
|
|
11699
|
+
operator: "=",
|
|
11700
|
+
left: id.type === "Identifier" ? id : id.left,
|
|
11701
|
+
right: last.argument,
|
|
11702
|
+
},
|
|
11703
|
+
};
|
|
11704
|
+
}
|
|
11705
|
+
else {
|
|
11706
|
+
const side_exprs = unused(last.argument);
|
|
11707
|
+
block.body.splice(block.body.length - 1, 1, ...side_exprs);
|
|
11708
|
+
}
|
|
11404
11709
|
}
|
|
11405
11710
|
else {
|
|
11406
|
-
--
|
|
11711
|
+
--block.body.length;
|
|
11407
11712
|
}
|
|
11408
11713
|
}
|
|
11409
11714
|
return body;
|
|
@@ -11522,87 +11827,1461 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
11522
11827
|
return null;
|
|
11523
11828
|
}
|
|
11524
11829
|
|
|
11525
|
-
;// CONCATENATED MODULE: ./src/
|
|
11830
|
+
;// CONCATENATED MODULE: ./src/control-flow.ts
|
|
11526
11831
|
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11832
|
+
|
|
11833
|
+
const Terminals = {
|
|
11834
|
+
BreakStatement: "break",
|
|
11835
|
+
ContinueStatement: "continue",
|
|
11836
|
+
ReturnStatement: null,
|
|
11837
|
+
ThrowStatement: "throw",
|
|
11838
|
+
};
|
|
11839
|
+
class LocalState {
|
|
11840
|
+
constructor(func) {
|
|
11841
|
+
this.stack = [];
|
|
11842
|
+
this.info = new Map();
|
|
11843
|
+
this.curBlock = {};
|
|
11844
|
+
this.unreachable = false;
|
|
11845
|
+
this.push(func);
|
|
11846
|
+
}
|
|
11847
|
+
push(node) {
|
|
11848
|
+
const top = { node };
|
|
11849
|
+
this.stack.push(top);
|
|
11850
|
+
return top;
|
|
11851
|
+
}
|
|
11852
|
+
pop() {
|
|
11853
|
+
return this.stack.pop();
|
|
11854
|
+
}
|
|
11855
|
+
top(depth) {
|
|
11856
|
+
return this.stack[this.stack.length - (depth || 1)];
|
|
11857
|
+
}
|
|
11858
|
+
addEdge(from, to) {
|
|
11859
|
+
if (!from.succs) {
|
|
11860
|
+
from.succs = [to];
|
|
11861
|
+
}
|
|
11862
|
+
else {
|
|
11863
|
+
(0,external_util_cjs_namespaceObject.pushUnique)(from.succs, to);
|
|
11864
|
+
}
|
|
11865
|
+
if (!to.preds) {
|
|
11866
|
+
to.preds = [from];
|
|
11867
|
+
}
|
|
11868
|
+
else {
|
|
11869
|
+
(0,external_util_cjs_namespaceObject.pushUnique)(to.preds, from);
|
|
11870
|
+
}
|
|
11871
|
+
}
|
|
11872
|
+
newBlock(block) {
|
|
11873
|
+
if (!block)
|
|
11874
|
+
block = {};
|
|
11875
|
+
if (!this.unreachable) {
|
|
11876
|
+
this.addEdge(this.curBlock, block);
|
|
11877
|
+
}
|
|
11878
|
+
this.unreachable = false;
|
|
11879
|
+
for (let i = this.stack.length; i--;) {
|
|
11880
|
+
const si = this.stack[i];
|
|
11881
|
+
if (si.throw) {
|
|
11882
|
+
block.exsucc = si.throw;
|
|
11883
|
+
if (!si.throw.expreds) {
|
|
11884
|
+
si.throw.expreds = [block];
|
|
11533
11885
|
}
|
|
11886
|
+
else {
|
|
11887
|
+
si.throw.expreds.push(block);
|
|
11888
|
+
}
|
|
11889
|
+
break;
|
|
11534
11890
|
}
|
|
11535
11891
|
}
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11892
|
+
return (this.curBlock = block);
|
|
11893
|
+
}
|
|
11894
|
+
terminal(type) {
|
|
11895
|
+
const re = Terminals[type];
|
|
11896
|
+
if (re) {
|
|
11897
|
+
for (let i = this.stack.length; i--;) {
|
|
11898
|
+
const target = this.stack[i][re];
|
|
11899
|
+
if (target) {
|
|
11900
|
+
this.addEdge(this.curBlock, target);
|
|
11901
|
+
break;
|
|
11902
|
+
}
|
|
11539
11903
|
}
|
|
11540
11904
|
}
|
|
11541
|
-
|
|
11542
|
-
}
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11905
|
+
this.unreachable = true;
|
|
11906
|
+
}
|
|
11907
|
+
}
|
|
11908
|
+
function buildReducedGraph(state, func, notice) {
|
|
11909
|
+
const { stack, pre, post } = state;
|
|
11910
|
+
try {
|
|
11911
|
+
const localState = new LocalState(func.node);
|
|
11912
|
+
const ret = localState.curBlock;
|
|
11913
|
+
state.stack = func.stack;
|
|
11914
|
+
const stmtStack = [func.node];
|
|
11915
|
+
let tryActive = 0;
|
|
11916
|
+
state.pre = (node) => {
|
|
11917
|
+
if (state.inType || localState.unreachable) {
|
|
11546
11918
|
return [];
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11919
|
+
}
|
|
11920
|
+
if (!localState.curBlock.node &&
|
|
11921
|
+
(isStatement(node) || isExpression(node))) {
|
|
11922
|
+
localState.curBlock.node = node;
|
|
11923
|
+
}
|
|
11924
|
+
if (isStatement(node)) {
|
|
11925
|
+
stmtStack.push(node);
|
|
11926
|
+
}
|
|
11927
|
+
switch (node.type) {
|
|
11928
|
+
case "AttributeList":
|
|
11550
11929
|
return [];
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
computed: false,
|
|
11566
|
-
}), node.right.argument);
|
|
11930
|
+
case "SwitchStatement": {
|
|
11931
|
+
const top = localState.push(node);
|
|
11932
|
+
top.break = {};
|
|
11933
|
+
state.traverse(node.discriminant);
|
|
11934
|
+
const testBlocks = [];
|
|
11935
|
+
let defaultSeen = false;
|
|
11936
|
+
node.cases.forEach((sc, i) => {
|
|
11937
|
+
if (sc.test) {
|
|
11938
|
+
state.traverse(sc.test);
|
|
11939
|
+
testBlocks[i] = localState.curBlock;
|
|
11940
|
+
localState.newBlock();
|
|
11941
|
+
}
|
|
11942
|
+
else {
|
|
11943
|
+
defaultSeen = true;
|
|
11567
11944
|
}
|
|
11945
|
+
});
|
|
11946
|
+
const endOfTests = localState.curBlock;
|
|
11947
|
+
if (!defaultSeen) {
|
|
11948
|
+
localState.addEdge(endOfTests, top.break);
|
|
11568
11949
|
}
|
|
11950
|
+
localState.unreachable = true;
|
|
11951
|
+
node.cases.forEach((sc, i) => {
|
|
11952
|
+
localState.newBlock();
|
|
11953
|
+
localState.addEdge(testBlocks[i] || endOfTests, localState.curBlock);
|
|
11954
|
+
sc.consequent.every((s) => {
|
|
11955
|
+
state.traverse(s);
|
|
11956
|
+
return !localState.unreachable;
|
|
11957
|
+
});
|
|
11958
|
+
});
|
|
11959
|
+
localState.newBlock(top.break);
|
|
11960
|
+
localState.unreachable = !top.break.preds;
|
|
11961
|
+
return [];
|
|
11569
11962
|
}
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
if (
|
|
11578
|
-
|
|
11579
|
-
|
|
11963
|
+
case "DoWhileStatement":
|
|
11964
|
+
case "WhileStatement": {
|
|
11965
|
+
localState.push(node);
|
|
11966
|
+
const top = localState.top();
|
|
11967
|
+
top.break = {};
|
|
11968
|
+
top.continue = {};
|
|
11969
|
+
let head;
|
|
11970
|
+
if (node.type === "WhileStatement") {
|
|
11971
|
+
head = localState.newBlock(top.continue);
|
|
11972
|
+
state.traverse(node.test);
|
|
11973
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
11974
|
+
localState.newBlock();
|
|
11580
11975
|
}
|
|
11581
|
-
|
|
11976
|
+
else {
|
|
11977
|
+
head = localState.newBlock();
|
|
11978
|
+
}
|
|
11979
|
+
state.traverse(node.body);
|
|
11980
|
+
if (node.type === "DoWhileStatement") {
|
|
11981
|
+
localState.newBlock(top.continue);
|
|
11982
|
+
state.traverse(node.test);
|
|
11983
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
11984
|
+
}
|
|
11985
|
+
localState.addEdge(localState.curBlock, head);
|
|
11986
|
+
localState.curBlock = top.break;
|
|
11987
|
+
return [];
|
|
11582
11988
|
}
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11989
|
+
case "TryStatement": {
|
|
11990
|
+
const top = localState.push(node);
|
|
11991
|
+
const catches = (top.throw = {});
|
|
11992
|
+
// This edge shouldn't exist, but we can trigger
|
|
11993
|
+
// (incorrect) "variable may not be initialized" errors
|
|
11994
|
+
// in the monkey c compiler without it.
|
|
11995
|
+
// https://forums.garmin.com/developer/connect-iq/i/bug-reports/incorrect-maybe-uninitialized-error
|
|
11996
|
+
localState.addEdge(localState.curBlock, top.throw);
|
|
11997
|
+
localState.newBlock();
|
|
11998
|
+
tryActive++;
|
|
11999
|
+
state.traverse(node.block);
|
|
12000
|
+
tryActive--;
|
|
12001
|
+
delete top.throw;
|
|
12002
|
+
top.posttry = {};
|
|
12003
|
+
const tryFallsThrough = !localState.unreachable;
|
|
12004
|
+
if (node.finalizer) {
|
|
12005
|
+
tryActive++;
|
|
12006
|
+
top.throw = top.finally = {};
|
|
12007
|
+
// curBlock branches to finally, no matter how it exits.
|
|
12008
|
+
localState.addEdge(localState.curBlock, top.finally);
|
|
12009
|
+
}
|
|
12010
|
+
else {
|
|
12011
|
+
if (!localState.unreachable) {
|
|
12012
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
12013
|
+
}
|
|
12014
|
+
}
|
|
12015
|
+
localState.unreachable = true;
|
|
12016
|
+
localState.newBlock(catches);
|
|
12017
|
+
if (node.handler) {
|
|
12018
|
+
state.traverse(node.handler);
|
|
12019
|
+
if (top.throw) {
|
|
12020
|
+
tryActive--;
|
|
12021
|
+
delete top.throw;
|
|
12022
|
+
}
|
|
12023
|
+
// Each "catch (ex instanceof Foo)" chains to the next,
|
|
12024
|
+
// but "catch (ex)" terminates the list. If the end
|
|
12025
|
+
// of the chain has a predecessor, its possible that
|
|
12026
|
+
// none of the conditions matched, so the exception
|
|
12027
|
+
// will propagate from there.
|
|
12028
|
+
if (localState.curBlock.preds) {
|
|
12029
|
+
localState.terminal("ThrowStatement");
|
|
12030
|
+
}
|
|
12031
|
+
}
|
|
12032
|
+
if (top.throw) {
|
|
12033
|
+
tryActive--;
|
|
12034
|
+
delete top.throw;
|
|
12035
|
+
}
|
|
12036
|
+
if (node.finalizer) {
|
|
12037
|
+
localState.unreachable = true;
|
|
12038
|
+
localState.newBlock(top.finally);
|
|
12039
|
+
delete top.finally;
|
|
12040
|
+
state.traverse(node.finalizer);
|
|
12041
|
+
if (tryFallsThrough && !localState.unreachable) {
|
|
12042
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
12043
|
+
}
|
|
12044
|
+
localState.terminal("ThrowStatement");
|
|
12045
|
+
}
|
|
12046
|
+
localState.unreachable = true;
|
|
12047
|
+
localState.newBlock(top.posttry);
|
|
12048
|
+
return [];
|
|
11587
12049
|
}
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
return checkResults(state.lookup(node), node) || ["object"];
|
|
12050
|
+
case "CatchClause": {
|
|
12051
|
+
const top = localState.top();
|
|
12052
|
+
if (!localState.curBlock.preds && !localState.curBlock.expreds) {
|
|
12053
|
+
return [];
|
|
11593
12054
|
}
|
|
11594
|
-
|
|
12055
|
+
const next = {};
|
|
12056
|
+
if (node.param && node.param.type === "BinaryExpression") {
|
|
12057
|
+
state.traverse(node.param);
|
|
12058
|
+
localState.addEdge(localState.curBlock, next);
|
|
12059
|
+
localState.newBlock();
|
|
12060
|
+
}
|
|
12061
|
+
state.traverse(node.body);
|
|
12062
|
+
if (top.finally) {
|
|
12063
|
+
// this edge exists even if this point is unreachable
|
|
12064
|
+
localState.addEdge(localState.curBlock, top.finally);
|
|
12065
|
+
}
|
|
12066
|
+
if (!localState.unreachable) {
|
|
12067
|
+
if (!top.posttry)
|
|
12068
|
+
top.posttry = {};
|
|
12069
|
+
localState.addEdge(localState.curBlock, top.posttry);
|
|
12070
|
+
}
|
|
12071
|
+
localState.unreachable = true;
|
|
12072
|
+
localState.newBlock(next);
|
|
12073
|
+
return [];
|
|
11595
12074
|
}
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
12075
|
+
case "ForStatement": {
|
|
12076
|
+
const top = localState.push(node);
|
|
12077
|
+
if (node.init)
|
|
12078
|
+
state.traverse(node.init);
|
|
12079
|
+
const head = localState.newBlock();
|
|
12080
|
+
top.break = {};
|
|
12081
|
+
top.continue = {};
|
|
12082
|
+
if (node.test) {
|
|
12083
|
+
state.traverse(node.test);
|
|
12084
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
12085
|
+
localState.newBlock();
|
|
12086
|
+
}
|
|
12087
|
+
state.traverse(node.body);
|
|
12088
|
+
localState.newBlock(top.continue);
|
|
12089
|
+
if (node.update) {
|
|
12090
|
+
state.traverse(node.update);
|
|
12091
|
+
}
|
|
12092
|
+
if (!localState.unreachable) {
|
|
12093
|
+
localState.addEdge(localState.curBlock, head);
|
|
12094
|
+
}
|
|
12095
|
+
// there is no fall through from the end of the loop
|
|
12096
|
+
// to the next block. The only way there is via break
|
|
12097
|
+
// or the test failing.
|
|
12098
|
+
localState.unreachable = true;
|
|
12099
|
+
localState.newBlock(top.break);
|
|
12100
|
+
if (!top.break.preds) {
|
|
12101
|
+
localState.unreachable = true;
|
|
12102
|
+
}
|
|
12103
|
+
return [];
|
|
11600
12104
|
}
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
12105
|
+
case "IfStatement":
|
|
12106
|
+
case "ConditionalExpression": {
|
|
12107
|
+
state.traverse(node.test);
|
|
12108
|
+
const alternate = {};
|
|
12109
|
+
localState.addEdge(localState.curBlock, alternate);
|
|
12110
|
+
localState.newBlock();
|
|
12111
|
+
state.traverse(node.consequent);
|
|
12112
|
+
const consequent = localState.unreachable
|
|
12113
|
+
? null
|
|
12114
|
+
: localState.curBlock;
|
|
12115
|
+
localState.unreachable = true;
|
|
12116
|
+
localState.newBlock(alternate);
|
|
12117
|
+
if (node.alternate) {
|
|
12118
|
+
state.traverse(node.alternate);
|
|
12119
|
+
if (!localState.unreachable) {
|
|
12120
|
+
localState.newBlock();
|
|
12121
|
+
}
|
|
12122
|
+
}
|
|
12123
|
+
if (consequent) {
|
|
12124
|
+
if (localState.unreachable) {
|
|
12125
|
+
localState.newBlock();
|
|
12126
|
+
}
|
|
12127
|
+
localState.addEdge(consequent, localState.curBlock);
|
|
12128
|
+
}
|
|
12129
|
+
return [];
|
|
12130
|
+
}
|
|
12131
|
+
case "LogicalExpression": {
|
|
12132
|
+
state.traverse(node.left);
|
|
12133
|
+
if (localState.unreachable)
|
|
12134
|
+
break;
|
|
12135
|
+
const mid = localState.curBlock;
|
|
12136
|
+
localState.newBlock();
|
|
12137
|
+
state.traverse(node.right);
|
|
12138
|
+
localState.newBlock();
|
|
12139
|
+
localState.addEdge(mid, localState.curBlock);
|
|
12140
|
+
return [];
|
|
12141
|
+
}
|
|
12142
|
+
case "VariableDeclarator":
|
|
12143
|
+
return ["init"];
|
|
12144
|
+
case "MemberExpression":
|
|
12145
|
+
if (!node.computed) {
|
|
12146
|
+
return ["object"];
|
|
12147
|
+
}
|
|
12148
|
+
break;
|
|
12149
|
+
case "UnaryExpression":
|
|
12150
|
+
if (node.operator === ":") {
|
|
12151
|
+
return [];
|
|
12152
|
+
}
|
|
12153
|
+
break;
|
|
12154
|
+
case "UpdateExpression":
|
|
12155
|
+
// We don't want to traverse the argument, since then it would
|
|
12156
|
+
// look like a ref, rather than a def. But if its a
|
|
12157
|
+
// MemberExpression, we *do* want to traverse the subexpressions
|
|
12158
|
+
// as potential refs.
|
|
12159
|
+
if (node.argument.type === "MemberExpression") {
|
|
12160
|
+
state.traverse(node.argument.object);
|
|
12161
|
+
if (node.argument.computed) {
|
|
12162
|
+
state.traverse(node.argument.property);
|
|
12163
|
+
}
|
|
12164
|
+
}
|
|
12165
|
+
return [];
|
|
12166
|
+
case "AssignmentExpression":
|
|
12167
|
+
if (node.left.type === "MemberExpression") {
|
|
12168
|
+
state.traverse(node.left.object);
|
|
12169
|
+
if (node.left.computed) {
|
|
12170
|
+
state.traverse(node.left.property);
|
|
12171
|
+
}
|
|
12172
|
+
}
|
|
12173
|
+
return ["right"];
|
|
12174
|
+
case "ThrowStatement":
|
|
12175
|
+
case "ReturnStatement":
|
|
12176
|
+
if (node.argument) {
|
|
12177
|
+
state.traverse(node.argument);
|
|
12178
|
+
}
|
|
12179
|
+
// fall through
|
|
12180
|
+
case "BreakStatement":
|
|
12181
|
+
case "ContinueStatement":
|
|
12182
|
+
localState.terminal(node.type);
|
|
12183
|
+
return [];
|
|
12184
|
+
}
|
|
12185
|
+
return null;
|
|
12186
|
+
};
|
|
12187
|
+
const addEvent = (block, event) => {
|
|
12188
|
+
if (!block.events) {
|
|
12189
|
+
block.events = [event];
|
|
12190
|
+
}
|
|
12191
|
+
else {
|
|
12192
|
+
block.events.push(event);
|
|
12193
|
+
}
|
|
12194
|
+
};
|
|
12195
|
+
state.post = (node) => {
|
|
12196
|
+
const curStmt = stmtStack[stmtStack.length - 1];
|
|
12197
|
+
if (!state.inType) {
|
|
12198
|
+
const throws = tryActive > 0 && mayThrow(node);
|
|
12199
|
+
const event = notice(node, curStmt, throws);
|
|
12200
|
+
if (throws) {
|
|
12201
|
+
if (!event) {
|
|
12202
|
+
throw new Error("mayThrow expression in try/catch must generate an event");
|
|
12203
|
+
}
|
|
12204
|
+
}
|
|
12205
|
+
else if (event) {
|
|
12206
|
+
event.mayThrow = false;
|
|
12207
|
+
}
|
|
12208
|
+
if (event) {
|
|
12209
|
+
if (event.mayThrow) {
|
|
12210
|
+
for (let i = localState.stack.length; i--;) {
|
|
12211
|
+
const target = localState.stack[i].throw;
|
|
12212
|
+
if (target) {
|
|
12213
|
+
if (localState.curBlock.exsucc) {
|
|
12214
|
+
if (localState.curBlock.exsucc !== target) {
|
|
12215
|
+
throw new Error(`Block has multiple throw targets`);
|
|
12216
|
+
}
|
|
12217
|
+
}
|
|
12218
|
+
else {
|
|
12219
|
+
localState.curBlock.exsucc = target;
|
|
12220
|
+
if (!target.expreds) {
|
|
12221
|
+
target.expreds = [localState.curBlock];
|
|
12222
|
+
}
|
|
12223
|
+
else {
|
|
12224
|
+
target.expreds.push(localState.curBlock);
|
|
12225
|
+
}
|
|
12226
|
+
}
|
|
12227
|
+
break;
|
|
12228
|
+
}
|
|
12229
|
+
}
|
|
12230
|
+
}
|
|
12231
|
+
addEvent(localState.curBlock, event);
|
|
12232
|
+
}
|
|
12233
|
+
}
|
|
12234
|
+
if (curStmt === node) {
|
|
12235
|
+
stmtStack.pop();
|
|
12236
|
+
}
|
|
12237
|
+
if (localState.top().node === node) {
|
|
12238
|
+
localState.pop();
|
|
12239
|
+
}
|
|
12240
|
+
return null;
|
|
12241
|
+
};
|
|
12242
|
+
state.traverse(func.node);
|
|
12243
|
+
return cleanCfg(ret);
|
|
12244
|
+
}
|
|
12245
|
+
finally {
|
|
12246
|
+
state.pre = pre;
|
|
12247
|
+
state.post = post;
|
|
12248
|
+
state.stack = stack;
|
|
12249
|
+
}
|
|
12250
|
+
}
|
|
12251
|
+
function cleanCfg(head) {
|
|
12252
|
+
preOrderTraverse(head, (cur) => {
|
|
12253
|
+
if (cur.succs && cur.succs.length === 1) {
|
|
12254
|
+
const succ = cur.succs[0];
|
|
12255
|
+
if (succ !== head &&
|
|
12256
|
+
succ.preds.length === 1 &&
|
|
12257
|
+
(!cur.exsucc || cur.exsucc === succ.exsucc) &&
|
|
12258
|
+
(!succ.succs ||
|
|
12259
|
+
succ.succs.length === 1 ||
|
|
12260
|
+
(cur.preds && cur.preds.length === 1))) {
|
|
12261
|
+
if (cur.events) {
|
|
12262
|
+
if (succ.events) {
|
|
12263
|
+
cur.events.push(...succ.events);
|
|
12264
|
+
}
|
|
12265
|
+
}
|
|
12266
|
+
else if (succ.events) {
|
|
12267
|
+
cur.events = succ.events;
|
|
12268
|
+
}
|
|
12269
|
+
if (succ.exsucc) {
|
|
12270
|
+
const preds = succ.exsucc.expreds;
|
|
12271
|
+
for (let i = preds.length; i--;) {
|
|
12272
|
+
if (preds[i] === succ) {
|
|
12273
|
+
// If cur has an exsucc, we already
|
|
12274
|
+
// checked that its the same as succ's,
|
|
12275
|
+
// so we can just delete the edge.
|
|
12276
|
+
// Otherwise, we need to point it at cur.
|
|
12277
|
+
if (cur.exsucc) {
|
|
12278
|
+
preds.splice(i, 1);
|
|
12279
|
+
}
|
|
12280
|
+
else {
|
|
12281
|
+
preds[i] = cur;
|
|
12282
|
+
}
|
|
12283
|
+
}
|
|
12284
|
+
}
|
|
12285
|
+
}
|
|
12286
|
+
cur.exsucc = succ.exsucc;
|
|
12287
|
+
cur.succs = succ.succs;
|
|
12288
|
+
if (cur.succs) {
|
|
12289
|
+
cur.succs.forEach((s) => s.preds.forEach((p, i, arr) => {
|
|
12290
|
+
if (p === succ) {
|
|
12291
|
+
arr[i] = cur;
|
|
12292
|
+
}
|
|
12293
|
+
}));
|
|
12294
|
+
}
|
|
12295
|
+
if (!cur.node)
|
|
12296
|
+
cur.node = succ.node;
|
|
12297
|
+
}
|
|
12298
|
+
}
|
|
12299
|
+
});
|
|
12300
|
+
return head;
|
|
12301
|
+
}
|
|
12302
|
+
function postOrderTraverse(head, visitor) {
|
|
12303
|
+
const visited = new Set();
|
|
12304
|
+
const helper = (cur) => {
|
|
12305
|
+
if (visited.has(cur))
|
|
12306
|
+
return;
|
|
12307
|
+
visited.add(cur);
|
|
12308
|
+
if (cur.succs) {
|
|
12309
|
+
cur.succs.forEach((block) => helper(block));
|
|
12310
|
+
}
|
|
12311
|
+
if (cur.exsucc)
|
|
12312
|
+
helper(cur.exsucc);
|
|
12313
|
+
visitor(cur);
|
|
12314
|
+
};
|
|
12315
|
+
helper(head);
|
|
12316
|
+
}
|
|
12317
|
+
function preOrderTraverse(head, visitor) {
|
|
12318
|
+
const visited = new Set();
|
|
12319
|
+
const helper = (cur) => {
|
|
12320
|
+
if (visited.has(cur))
|
|
12321
|
+
return;
|
|
12322
|
+
visited.add(cur);
|
|
12323
|
+
visitor(cur);
|
|
12324
|
+
if (cur.succs) {
|
|
12325
|
+
cur.succs.forEach((block) => helper(block));
|
|
12326
|
+
}
|
|
12327
|
+
if (cur.exsucc)
|
|
12328
|
+
helper(cur.exsucc);
|
|
12329
|
+
};
|
|
12330
|
+
helper(head);
|
|
12331
|
+
}
|
|
12332
|
+
function getPostOrder(head) {
|
|
12333
|
+
const blocks = [];
|
|
12334
|
+
postOrderTraverse(head, (block) => blocks.push(block));
|
|
12335
|
+
return blocks;
|
|
12336
|
+
}
|
|
12337
|
+
function getPreOrder(head) {
|
|
12338
|
+
const blocks = [];
|
|
12339
|
+
postOrderTraverse(head, (block) => blocks.push(block));
|
|
12340
|
+
return blocks;
|
|
12341
|
+
}
|
|
12342
|
+
|
|
12343
|
+
// EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
|
|
12344
|
+
var priorityqueuejs = __webpack_require__(2789);
|
|
12345
|
+
;// CONCATENATED MODULE: ./src/pre.ts
|
|
12346
|
+
|
|
12347
|
+
|
|
12348
|
+
|
|
12349
|
+
|
|
12350
|
+
/**
|
|
12351
|
+
* This implements a pseudo Partial Redundancy Elimination
|
|
12352
|
+
* pass. It isn't quite like traditional PRE because we're
|
|
12353
|
+
* aiming to minimize size, not dynamic instructions. So
|
|
12354
|
+
* for us, its worthwhile to take something like:
|
|
12355
|
+
*
|
|
12356
|
+
* switch (x) {
|
|
12357
|
+
* case 1: foo(A.B); break;
|
|
12358
|
+
* case 2: foo(C); break;
|
|
12359
|
+
* case 3: bar(A.B); break;
|
|
12360
|
+
* }
|
|
12361
|
+
*
|
|
12362
|
+
* and rewrite it as
|
|
12363
|
+
*
|
|
12364
|
+
* var tmp = A.B;
|
|
12365
|
+
* switch (x) {
|
|
12366
|
+
* case 1: foo(tmp); break;
|
|
12367
|
+
* case 2: foo(C); break;
|
|
12368
|
+
* case 3: bar(tmp); break;
|
|
12369
|
+
* }
|
|
12370
|
+
*
|
|
12371
|
+
* because even though A.B wasn't used on all paths where we
|
|
12372
|
+
* inserted the temporary, we still reduced the code size.
|
|
12373
|
+
*/
|
|
12374
|
+
const logging = false;
|
|
12375
|
+
function declFullName(decl) {
|
|
12376
|
+
switch (decl.type) {
|
|
12377
|
+
case "Literal":
|
|
12378
|
+
return decl.raw || decl.value?.toString() || "null";
|
|
12379
|
+
case "VariableDeclarator":
|
|
12380
|
+
return decl.fullName;
|
|
12381
|
+
default:
|
|
12382
|
+
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
12383
|
+
}
|
|
12384
|
+
}
|
|
12385
|
+
function declName(decl) {
|
|
12386
|
+
switch (decl.type) {
|
|
12387
|
+
case "Literal":
|
|
12388
|
+
return (decl.raw || decl.value?.toString() || "null").replace(/[^\w]/g, "_");
|
|
12389
|
+
case "VariableDeclarator":
|
|
12390
|
+
return decl.name;
|
|
12391
|
+
default:
|
|
12392
|
+
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
12393
|
+
}
|
|
12394
|
+
}
|
|
12395
|
+
function logAntState(s, decl) {
|
|
12396
|
+
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
12397
|
+
if (event.type === "def" || event.type === "mod")
|
|
12398
|
+
defs++;
|
|
12399
|
+
return defs;
|
|
12400
|
+
}, 0);
|
|
12401
|
+
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
|
|
12402
|
+
console.log(` - members: ${Array.from(s.members)
|
|
12403
|
+
.map(([block, live]) => block.order + (live ? "t" : "f"))
|
|
12404
|
+
.join(", ")}`);
|
|
12405
|
+
}
|
|
12406
|
+
function logAntDecls(antDecls) {
|
|
12407
|
+
antDecls.forEach(logAntState);
|
|
12408
|
+
}
|
|
12409
|
+
function sizeBasedPRE(state, func) {
|
|
12410
|
+
if (!func.node.body)
|
|
12411
|
+
return;
|
|
12412
|
+
if (!state.config ||
|
|
12413
|
+
!state.config.sizeBasedPRE ||
|
|
12414
|
+
(typeof state.config.sizeBasedPRE === "string" &&
|
|
12415
|
+
state.config.sizeBasedPRE !== func.fullName)) {
|
|
12416
|
+
return;
|
|
12417
|
+
}
|
|
12418
|
+
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
12419
|
+
const candidates = computeAttributes(head);
|
|
12420
|
+
if (candidates) {
|
|
12421
|
+
if (logging) {
|
|
12422
|
+
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
12423
|
+
logAntDecls(candidates);
|
|
12424
|
+
}
|
|
12425
|
+
const nodeMap = new Map();
|
|
12426
|
+
const declMap = new Map();
|
|
12427
|
+
const variableDecl = withLoc({
|
|
12428
|
+
type: "VariableDeclaration",
|
|
12429
|
+
declarations: [],
|
|
12430
|
+
kind: "var",
|
|
12431
|
+
}, func.node.body);
|
|
12432
|
+
variableDecl.end = variableDecl.start;
|
|
12433
|
+
variableDecl.loc.end = variableDecl.loc.start;
|
|
12434
|
+
candidates.forEach((s, decl) => {
|
|
12435
|
+
let name;
|
|
12436
|
+
let i = 0;
|
|
12437
|
+
do {
|
|
12438
|
+
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
12439
|
+
if (!identifiers.has(name))
|
|
12440
|
+
break;
|
|
12441
|
+
i++;
|
|
12442
|
+
} while (true);
|
|
12443
|
+
declMap.set(decl, name);
|
|
12444
|
+
variableDecl.declarations.push(withLoc({
|
|
12445
|
+
type: "VariableDeclarator",
|
|
12446
|
+
id: withLoc({ type: "Identifier", name }, variableDecl),
|
|
12447
|
+
kind: "var",
|
|
12448
|
+
}, variableDecl));
|
|
12449
|
+
s.ant.forEach((event) => {
|
|
12450
|
+
const events = nodeMap.get(event.node);
|
|
12451
|
+
if (!events) {
|
|
12452
|
+
nodeMap.set(event.node, [event]);
|
|
12453
|
+
}
|
|
12454
|
+
else {
|
|
12455
|
+
events.push(event);
|
|
12456
|
+
}
|
|
12457
|
+
});
|
|
12458
|
+
});
|
|
12459
|
+
applyReplacements(func.node, nodeMap, declMap);
|
|
12460
|
+
func.node.body.body.unshift(variableDecl);
|
|
12461
|
+
}
|
|
12462
|
+
}
|
|
12463
|
+
function unhandledExpression(node) {
|
|
12464
|
+
throw new Error(`Unhandled expression type: ${node.type}`);
|
|
12465
|
+
}
|
|
12466
|
+
function buildPREGraph(state, func) {
|
|
12467
|
+
const findDecl = (node) => {
|
|
12468
|
+
if (node.type === "Identifier" ||
|
|
12469
|
+
(node.type === "MemberExpression" && !node.computed)) {
|
|
12470
|
+
const [, results] = state.lookup(node);
|
|
12471
|
+
if (results &&
|
|
12472
|
+
results.length === 1 &&
|
|
12473
|
+
results[0].parent?.type != "BlockStatement" &&
|
|
12474
|
+
results[0].results.length === 1 &&
|
|
12475
|
+
results[0].results[0].type === "VariableDeclarator") {
|
|
12476
|
+
return results[0].results[0];
|
|
12477
|
+
}
|
|
12478
|
+
}
|
|
12479
|
+
return null;
|
|
12480
|
+
};
|
|
12481
|
+
const literals = new Map();
|
|
12482
|
+
const identifiers = new Set();
|
|
12483
|
+
const liveDefs = new Map();
|
|
12484
|
+
const liveStmts = new Map();
|
|
12485
|
+
const liveDef = (def, stmt) => {
|
|
12486
|
+
let curNodes = liveDefs.get(def);
|
|
12487
|
+
if (!curNodes) {
|
|
12488
|
+
liveDefs.set(def, (curNodes = new Set()));
|
|
12489
|
+
}
|
|
12490
|
+
curNodes.add(stmt);
|
|
12491
|
+
let defs = liveStmts.get(stmt);
|
|
12492
|
+
if (!defs) {
|
|
12493
|
+
liveStmts.set(stmt, (defs = new Map()));
|
|
12494
|
+
}
|
|
12495
|
+
defs.set(def, (defs.get(def) || 0) + 1);
|
|
12496
|
+
};
|
|
12497
|
+
return {
|
|
12498
|
+
identifiers,
|
|
12499
|
+
graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
|
|
12500
|
+
const defs = liveStmts.get(node);
|
|
12501
|
+
if (defs) {
|
|
12502
|
+
liveStmts.delete(node);
|
|
12503
|
+
defs.forEach((count, def) => {
|
|
12504
|
+
if (count > 1) {
|
|
12505
|
+
defs.set(def, count--);
|
|
12506
|
+
return;
|
|
12507
|
+
}
|
|
12508
|
+
const v = liveDefs.get(def);
|
|
12509
|
+
if (!v || !v.has(node)) {
|
|
12510
|
+
throw new Error(`No stmt in liveDef for ${def ? declFullName(def) : "null"}`);
|
|
12511
|
+
}
|
|
12512
|
+
v.delete(node);
|
|
12513
|
+
if (!v.size) {
|
|
12514
|
+
liveDefs.delete(def);
|
|
12515
|
+
}
|
|
12516
|
+
});
|
|
12517
|
+
}
|
|
12518
|
+
switch (node.type) {
|
|
12519
|
+
case "BinaryExpression":
|
|
12520
|
+
case "UnaryExpression":
|
|
12521
|
+
case "SizedArrayExpression":
|
|
12522
|
+
case "ArrayExpression":
|
|
12523
|
+
case "ObjectExpression":
|
|
12524
|
+
case "ThisExpression":
|
|
12525
|
+
case "LogicalExpression":
|
|
12526
|
+
case "ConditionalExpression":
|
|
12527
|
+
case "SequenceExpression":
|
|
12528
|
+
case "ParenthesizedExpression":
|
|
12529
|
+
break;
|
|
12530
|
+
case "Literal":
|
|
12531
|
+
if (!node.value && refCost(node) > LocalRefCost) {
|
|
12532
|
+
let decl = literals.get(node.value);
|
|
12533
|
+
if (!decl) {
|
|
12534
|
+
decl = node;
|
|
12535
|
+
literals.set(node.value, decl);
|
|
12536
|
+
}
|
|
12537
|
+
return {
|
|
12538
|
+
type: "ref",
|
|
12539
|
+
node,
|
|
12540
|
+
decl: decl,
|
|
12541
|
+
mayThrow,
|
|
12542
|
+
};
|
|
12543
|
+
}
|
|
12544
|
+
break;
|
|
12545
|
+
case "Identifier":
|
|
12546
|
+
identifiers.add(node.name);
|
|
12547
|
+
// fall through
|
|
12548
|
+
case "MemberExpression":
|
|
12549
|
+
{
|
|
12550
|
+
const decl = findDecl(node);
|
|
12551
|
+
if (decl && decl.type === "VariableDeclarator") {
|
|
12552
|
+
const defStmts = (decl.node.kind === "var" && liveDefs.get(null)) ||
|
|
12553
|
+
liveDefs.get(decl);
|
|
12554
|
+
if (defStmts) {
|
|
12555
|
+
break;
|
|
12556
|
+
/*
|
|
12557
|
+
// hold off on this for now. we need to communicate
|
|
12558
|
+
// which defs need to be fixed, which involves yet-another
|
|
12559
|
+
// table.
|
|
12560
|
+
|
|
12561
|
+
if (defStmts.size !== 1) break;
|
|
12562
|
+
const fixable = isFixableStmt([...defStmts][0]);
|
|
12563
|
+
if (fixable === false) break;
|
|
12564
|
+
cost += fixable;
|
|
12565
|
+
*/
|
|
12566
|
+
}
|
|
12567
|
+
return {
|
|
12568
|
+
type: "ref",
|
|
12569
|
+
node,
|
|
12570
|
+
decl,
|
|
12571
|
+
mayThrow,
|
|
12572
|
+
};
|
|
12573
|
+
}
|
|
12574
|
+
}
|
|
12575
|
+
break;
|
|
12576
|
+
case "VariableDeclarator": {
|
|
12577
|
+
const decl = findDecl(node.id.type === "BinaryExpression" ? node.id.left : node.id);
|
|
12578
|
+
if (decl) {
|
|
12579
|
+
liveDef(decl, stmt);
|
|
12580
|
+
return {
|
|
12581
|
+
type: "def",
|
|
12582
|
+
node,
|
|
12583
|
+
decl,
|
|
12584
|
+
mayThrow,
|
|
12585
|
+
};
|
|
12586
|
+
}
|
|
12587
|
+
break;
|
|
12588
|
+
}
|
|
12589
|
+
case "AssignmentExpression": {
|
|
12590
|
+
const decl = findDecl(node.left);
|
|
12591
|
+
if (decl) {
|
|
12592
|
+
liveDef(decl, stmt);
|
|
12593
|
+
return {
|
|
12594
|
+
type: "def",
|
|
12595
|
+
node,
|
|
12596
|
+
decl,
|
|
12597
|
+
mayThrow,
|
|
12598
|
+
};
|
|
12599
|
+
}
|
|
12600
|
+
break;
|
|
12601
|
+
}
|
|
12602
|
+
case "UpdateExpression": {
|
|
12603
|
+
const decl = findDecl(node.argument);
|
|
12604
|
+
if (decl) {
|
|
12605
|
+
liveDef(decl, stmt);
|
|
12606
|
+
return {
|
|
12607
|
+
type: "def",
|
|
12608
|
+
node,
|
|
12609
|
+
decl,
|
|
12610
|
+
mayThrow,
|
|
12611
|
+
};
|
|
12612
|
+
}
|
|
12613
|
+
break;
|
|
12614
|
+
}
|
|
12615
|
+
case "NewExpression":
|
|
12616
|
+
case "CallExpression":
|
|
12617
|
+
liveDef(null, stmt);
|
|
12618
|
+
return { type: "mod", node, mayThrow };
|
|
12619
|
+
default:
|
|
12620
|
+
if (!isExpression(node))
|
|
12621
|
+
break;
|
|
12622
|
+
unhandledExpression(node);
|
|
12623
|
+
}
|
|
12624
|
+
if (mayThrow) {
|
|
12625
|
+
return { type: "exn", node, mayThrow };
|
|
12626
|
+
}
|
|
12627
|
+
return null;
|
|
12628
|
+
}),
|
|
12629
|
+
};
|
|
12630
|
+
}
|
|
12631
|
+
function anticipatedDecls() {
|
|
12632
|
+
return new Map();
|
|
12633
|
+
}
|
|
12634
|
+
function cloneSet(ae) {
|
|
12635
|
+
return new Set(ae);
|
|
12636
|
+
}
|
|
12637
|
+
function mergeSet(a, b) {
|
|
12638
|
+
b.forEach((event) => a.add(event));
|
|
12639
|
+
}
|
|
12640
|
+
function equalSet(a, b) {
|
|
12641
|
+
if (a.size != b.size)
|
|
12642
|
+
return false;
|
|
12643
|
+
for (const item of a) {
|
|
12644
|
+
if (!b.has(item))
|
|
12645
|
+
return false;
|
|
12646
|
+
}
|
|
12647
|
+
return true;
|
|
12648
|
+
}
|
|
12649
|
+
function equalMap(a, b) {
|
|
12650
|
+
if (a.size != b.size)
|
|
12651
|
+
return false;
|
|
12652
|
+
for (const [item, value] of a) {
|
|
12653
|
+
if (b.get(item) !== value)
|
|
12654
|
+
return false;
|
|
12655
|
+
}
|
|
12656
|
+
return true;
|
|
12657
|
+
}
|
|
12658
|
+
function anticipatedState(node, events) {
|
|
12659
|
+
return { ant: events || new Set(), live: true, node, members: new Map() };
|
|
12660
|
+
}
|
|
12661
|
+
function cloneAnticipatedState(as) {
|
|
12662
|
+
return {
|
|
12663
|
+
ant: cloneSet(as.ant),
|
|
12664
|
+
live: as.live,
|
|
12665
|
+
node: as.node,
|
|
12666
|
+
members: new Map(as.members),
|
|
12667
|
+
};
|
|
12668
|
+
}
|
|
12669
|
+
function mergeAnticipatedState(ae, be) {
|
|
12670
|
+
mergeSet(ae.ant, be.ant);
|
|
12671
|
+
be.members.forEach((live, block) => ae.members.set(block, live));
|
|
12672
|
+
if (be.live)
|
|
12673
|
+
ae.live = true;
|
|
12674
|
+
}
|
|
12675
|
+
function cloneAnticipatedDecls(ad) {
|
|
12676
|
+
const copy = anticipatedDecls();
|
|
12677
|
+
for (const [k, v] of ad) {
|
|
12678
|
+
if (!v.isIsolated) {
|
|
12679
|
+
copy.set(k, cloneAnticipatedState(v));
|
|
12680
|
+
}
|
|
12681
|
+
}
|
|
12682
|
+
return copy;
|
|
12683
|
+
}
|
|
12684
|
+
function mergeAnticipatedDecls(a, b) {
|
|
12685
|
+
for (const [k, v] of b) {
|
|
12686
|
+
if (v.isIsolated)
|
|
12687
|
+
continue;
|
|
12688
|
+
const ae = a.get(k);
|
|
12689
|
+
if (ae) {
|
|
12690
|
+
mergeAnticipatedState(ae, v);
|
|
12691
|
+
}
|
|
12692
|
+
else {
|
|
12693
|
+
a.set(k, cloneAnticipatedState(v));
|
|
12694
|
+
}
|
|
12695
|
+
}
|
|
12696
|
+
}
|
|
12697
|
+
function equalStates(a, b) {
|
|
12698
|
+
if (a.size !== b.size)
|
|
12699
|
+
return false;
|
|
12700
|
+
for (const [k, ae] of a) {
|
|
12701
|
+
const be = b.get(k);
|
|
12702
|
+
if (!be ||
|
|
12703
|
+
be.live != ae.live ||
|
|
12704
|
+
be.isIsolated != ae.isIsolated ||
|
|
12705
|
+
!equalSet(ae.ant, be.ant) ||
|
|
12706
|
+
!equalMap(ae.members, be.members)) {
|
|
12707
|
+
return false;
|
|
12708
|
+
}
|
|
12709
|
+
}
|
|
12710
|
+
return true;
|
|
12711
|
+
}
|
|
12712
|
+
const LocalRefCost = 2;
|
|
12713
|
+
function refCost(node) {
|
|
12714
|
+
if (node.type === "Literal") {
|
|
12715
|
+
switch (typeof node.value) {
|
|
12716
|
+
case "string":
|
|
12717
|
+
return 5;
|
|
12718
|
+
case "number":
|
|
12719
|
+
return 5;
|
|
12720
|
+
case "boolean":
|
|
12721
|
+
return 2;
|
|
12722
|
+
default:
|
|
12723
|
+
if (node.value === null) {
|
|
12724
|
+
return 2;
|
|
12725
|
+
}
|
|
12726
|
+
return 0;
|
|
12727
|
+
}
|
|
12728
|
+
}
|
|
12729
|
+
// A read from a non-local identifier takes 8 bytes
|
|
12730
|
+
let cost = 8;
|
|
12731
|
+
if (node.type === "Identifier")
|
|
12732
|
+
return cost;
|
|
12733
|
+
while (true) {
|
|
12734
|
+
const next = node.object;
|
|
12735
|
+
if (next.type != "MemberExpression") {
|
|
12736
|
+
if (next.type != "ThisExpression") {
|
|
12737
|
+
cost += next.type === "Identifier" && next.name === "$" ? 4 : 6;
|
|
12738
|
+
}
|
|
12739
|
+
return cost;
|
|
12740
|
+
}
|
|
12741
|
+
node = next;
|
|
12742
|
+
cost += 6;
|
|
12743
|
+
}
|
|
12744
|
+
}
|
|
12745
|
+
function defCost(node) {
|
|
12746
|
+
return refCost(node) + 2;
|
|
12747
|
+
}
|
|
12748
|
+
function candidateBoundary(candState) {
|
|
12749
|
+
const boundary = new Set();
|
|
12750
|
+
candState.members.forEach((live, block) => {
|
|
12751
|
+
if (live && block !== candState.head) {
|
|
12752
|
+
if (block.preds) {
|
|
12753
|
+
block.preds.forEach((pred) => candState.members.has(pred) || boundary.add(pred));
|
|
12754
|
+
}
|
|
12755
|
+
}
|
|
12756
|
+
});
|
|
12757
|
+
if (candState.live) {
|
|
12758
|
+
if (!candState.head) {
|
|
12759
|
+
throw new Error(`Missing head`);
|
|
12760
|
+
}
|
|
12761
|
+
boundary.add(candState.head);
|
|
12762
|
+
}
|
|
12763
|
+
return boundary;
|
|
12764
|
+
}
|
|
12765
|
+
function candidateCost(candState) {
|
|
12766
|
+
let cost = 0;
|
|
12767
|
+
candState.ant.forEach((event) => {
|
|
12768
|
+
if (event.type === "ref") {
|
|
12769
|
+
cost -= refCost(candState.node) - LocalRefCost;
|
|
12770
|
+
}
|
|
12771
|
+
else {
|
|
12772
|
+
cost += defCost(candState.node);
|
|
12773
|
+
}
|
|
12774
|
+
});
|
|
12775
|
+
const boundarySize = candidateBoundary(candState).size;
|
|
12776
|
+
cost += defCost(candState.node) * boundarySize;
|
|
12777
|
+
return cost;
|
|
12778
|
+
}
|
|
12779
|
+
function computeAttributes(head) {
|
|
12780
|
+
const order = getPostOrder(head);
|
|
12781
|
+
order.forEach((block, i) => {
|
|
12782
|
+
block.order = i;
|
|
12783
|
+
});
|
|
12784
|
+
if (logging) {
|
|
12785
|
+
order.forEach((block) => {
|
|
12786
|
+
console.log(block.order, `(${block.node ? block.node.loc?.start.line : "??"})`, `Preds: ${(block.preds || [])
|
|
12787
|
+
.map((block) => block.order)
|
|
12788
|
+
.join(", ")}`);
|
|
12789
|
+
if (block.events) {
|
|
12790
|
+
block.events.forEach((event) => event.type !== "exn" &&
|
|
12791
|
+
console.log(` ${event.type}: ${event.decl ? declFullName(event.decl) : "??"}`));
|
|
12792
|
+
}
|
|
12793
|
+
console.log(`Succs: ${(block.succs || [])
|
|
12794
|
+
.map((block) => block.order)
|
|
12795
|
+
.join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
|
|
12796
|
+
});
|
|
12797
|
+
}
|
|
12798
|
+
const enqueued = new Set();
|
|
12799
|
+
const queue = new priorityqueuejs((b, a) => (a.order || 0) - (b.order || 0));
|
|
12800
|
+
const enqueue = (block) => {
|
|
12801
|
+
if (!enqueued.has(block)) {
|
|
12802
|
+
enqueued.add(block);
|
|
12803
|
+
queue.enq(block);
|
|
12804
|
+
}
|
|
12805
|
+
};
|
|
12806
|
+
const dequeue = () => {
|
|
12807
|
+
const block = queue.deq();
|
|
12808
|
+
enqueued.delete(block);
|
|
12809
|
+
return block;
|
|
12810
|
+
};
|
|
12811
|
+
const blockStates = [];
|
|
12812
|
+
/*
|
|
12813
|
+
Algorithm
|
|
12814
|
+
=========
|
|
12815
|
+
|
|
12816
|
+
Process blocks in post-order, and the events in reverse
|
|
12817
|
+
order to collect the AnticipatedState at the start of each
|
|
12818
|
+
Block.
|
|
12819
|
+
|
|
12820
|
+
Then for each EventDecl find the best starting block.
|
|
12821
|
+
*/
|
|
12822
|
+
const modMap = new Map();
|
|
12823
|
+
const getMod = (event, decl, id) => {
|
|
12824
|
+
if (id.type !== "Identifier" && id.type !== "MemberExpression") {
|
|
12825
|
+
throw new Error("Trying to modify a non-variable");
|
|
12826
|
+
}
|
|
12827
|
+
let eventMap = modMap.get(event);
|
|
12828
|
+
if (!eventMap) {
|
|
12829
|
+
modMap.set(event, (eventMap = new Map()));
|
|
12830
|
+
}
|
|
12831
|
+
let result = eventMap.get(decl);
|
|
12832
|
+
if (!result) {
|
|
12833
|
+
result = {
|
|
12834
|
+
type: "mod",
|
|
12835
|
+
node: event.node,
|
|
12836
|
+
decl,
|
|
12837
|
+
id,
|
|
12838
|
+
mayThrow: event.mayThrow,
|
|
12839
|
+
};
|
|
12840
|
+
eventMap.set(decl, result);
|
|
12841
|
+
}
|
|
12842
|
+
return result;
|
|
12843
|
+
};
|
|
12844
|
+
order.forEach((block) => enqueue(block));
|
|
12845
|
+
while (queue.size()) {
|
|
12846
|
+
const top = dequeue();
|
|
12847
|
+
if (top.order === undefined) {
|
|
12848
|
+
throw new Error(`Unreachable block was visited!`);
|
|
12849
|
+
}
|
|
12850
|
+
const curState = (top.succs &&
|
|
12851
|
+
top.succs.reduce((blockState, succ) => {
|
|
12852
|
+
const succState = blockStates[succ.order];
|
|
12853
|
+
if (succState) {
|
|
12854
|
+
if (!blockState) {
|
|
12855
|
+
blockState = cloneAnticipatedDecls(succState);
|
|
12856
|
+
}
|
|
12857
|
+
else {
|
|
12858
|
+
mergeAnticipatedDecls(blockState, succState);
|
|
12859
|
+
}
|
|
12860
|
+
}
|
|
12861
|
+
return blockState;
|
|
12862
|
+
}, null)) ||
|
|
12863
|
+
anticipatedDecls();
|
|
12864
|
+
if (top.events) {
|
|
12865
|
+
for (let i = top.events.length; i--;) {
|
|
12866
|
+
const event = top.events[i];
|
|
12867
|
+
if (event.mayThrow && top.exsucc) {
|
|
12868
|
+
const succState = blockStates[top.exsucc.order];
|
|
12869
|
+
if (succState) {
|
|
12870
|
+
mergeAnticipatedDecls(curState, succState);
|
|
12871
|
+
}
|
|
12872
|
+
}
|
|
12873
|
+
switch (event.type) {
|
|
12874
|
+
case "ref": {
|
|
12875
|
+
let candidates = curState.get(event.decl);
|
|
12876
|
+
if (!candidates) {
|
|
12877
|
+
candidates = anticipatedState(event.node);
|
|
12878
|
+
curState.set(event.decl, candidates);
|
|
12879
|
+
}
|
|
12880
|
+
candidates.ant.add(event);
|
|
12881
|
+
candidates.live = true;
|
|
12882
|
+
break;
|
|
12883
|
+
}
|
|
12884
|
+
case "mod": {
|
|
12885
|
+
curState.forEach((candidates, decl) => {
|
|
12886
|
+
if (decl.type === "VariableDeclarator" &&
|
|
12887
|
+
decl.node.kind === "var" &&
|
|
12888
|
+
candidates.live) {
|
|
12889
|
+
candidates.ant.add(getMod(event, decl, candidates.node));
|
|
12890
|
+
candidates.live = false;
|
|
12891
|
+
}
|
|
12892
|
+
});
|
|
12893
|
+
break;
|
|
12894
|
+
}
|
|
12895
|
+
case "def": {
|
|
12896
|
+
let candidates = curState.get(event.decl);
|
|
12897
|
+
const isUpdate = event.node.type === "UpdateExpression" ||
|
|
12898
|
+
(event.node.type === "AssignmentExpression" &&
|
|
12899
|
+
event.node.operator !== "=");
|
|
12900
|
+
if (!candidates) {
|
|
12901
|
+
const target = event.node.type === "AssignmentExpression"
|
|
12902
|
+
? event.node.left
|
|
12903
|
+
: event.node.type === "UpdateExpression"
|
|
12904
|
+
? event.node.argument
|
|
12905
|
+
: event.node.id.type === "BinaryExpression"
|
|
12906
|
+
? event.node.id.left
|
|
12907
|
+
: event.node.id;
|
|
12908
|
+
candidates = anticipatedState(target);
|
|
12909
|
+
curState.set(event.decl, candidates);
|
|
12910
|
+
}
|
|
12911
|
+
if (isUpdate || candidates.live) {
|
|
12912
|
+
candidates.ant.add(event);
|
|
12913
|
+
}
|
|
12914
|
+
candidates.live = isUpdate;
|
|
12915
|
+
break;
|
|
12916
|
+
}
|
|
12917
|
+
}
|
|
12918
|
+
}
|
|
12919
|
+
}
|
|
12920
|
+
curState.forEach((antState) => {
|
|
12921
|
+
antState.head = top;
|
|
12922
|
+
antState.members.set(top, antState.live);
|
|
12923
|
+
if (!antState.live && candidateBoundary(antState).size === 0) {
|
|
12924
|
+
// we found a group that's isolated from the rest
|
|
12925
|
+
// of the function. Don't merge it with earlier
|
|
12926
|
+
// refs and defs, because we can take it or leave
|
|
12927
|
+
// it based on its own cost.
|
|
12928
|
+
antState.isIsolated = true;
|
|
12929
|
+
}
|
|
12930
|
+
});
|
|
12931
|
+
const oldState = blockStates[top.order];
|
|
12932
|
+
if (oldState && equalStates(oldState, curState)) {
|
|
12933
|
+
continue;
|
|
12934
|
+
}
|
|
12935
|
+
blockStates[top.order] = curState;
|
|
12936
|
+
if (logging) {
|
|
12937
|
+
console.log(`Updated block ${top.order}`);
|
|
12938
|
+
logAntDecls(curState);
|
|
12939
|
+
}
|
|
12940
|
+
if (top.preds) {
|
|
12941
|
+
top.preds.forEach((pred) => enqueue(pred));
|
|
12942
|
+
}
|
|
12943
|
+
}
|
|
12944
|
+
const candidateDecls = anticipatedDecls();
|
|
12945
|
+
blockStates.forEach((blockState, i) => {
|
|
12946
|
+
blockState &&
|
|
12947
|
+
blockState.forEach((events, decl) => {
|
|
12948
|
+
const cost = candidateCost(events);
|
|
12949
|
+
if (cost >= 0)
|
|
12950
|
+
return;
|
|
12951
|
+
const existing = candidateDecls.get(decl);
|
|
12952
|
+
if (!existing ||
|
|
12953
|
+
existing.isIsolated ||
|
|
12954
|
+
candidateCost(existing) > cost) {
|
|
12955
|
+
const boundary = candidateBoundary(events);
|
|
12956
|
+
if (!Array.from(boundary).every((block) => {
|
|
12957
|
+
if (block !== events.head && block.events) {
|
|
12958
|
+
if (events.node.type === "Literal") {
|
|
12959
|
+
return false;
|
|
12960
|
+
}
|
|
12961
|
+
let i = block.events.length;
|
|
12962
|
+
while (i--) {
|
|
12963
|
+
const event = block.events[i];
|
|
12964
|
+
if (event.type === "def" || event.type === "mod") {
|
|
12965
|
+
events.ant.add({
|
|
12966
|
+
type: "mod",
|
|
12967
|
+
node: event.node,
|
|
12968
|
+
decl,
|
|
12969
|
+
id: events.node,
|
|
12970
|
+
mayThrow: false,
|
|
12971
|
+
});
|
|
12972
|
+
events.members.set(block, false);
|
|
12973
|
+
return true;
|
|
12974
|
+
}
|
|
12975
|
+
}
|
|
12976
|
+
}
|
|
12977
|
+
const node = block.node;
|
|
12978
|
+
if (!node)
|
|
12979
|
+
return false;
|
|
12980
|
+
events.ant.add({
|
|
12981
|
+
type: "mod",
|
|
12982
|
+
node: node.type === "FunctionDeclaration" ? node.body : node,
|
|
12983
|
+
before: true,
|
|
12984
|
+
decl,
|
|
12985
|
+
id: events.node,
|
|
12986
|
+
mayThrow: false,
|
|
12987
|
+
});
|
|
12988
|
+
events.members.set(block, false);
|
|
12989
|
+
return true;
|
|
12990
|
+
})) {
|
|
12991
|
+
return;
|
|
12992
|
+
}
|
|
12993
|
+
events.live = false;
|
|
12994
|
+
if (existing && existing.isIsolated) {
|
|
12995
|
+
delete existing.isIsolated;
|
|
12996
|
+
mergeAnticipatedState(events, existing);
|
|
12997
|
+
}
|
|
12998
|
+
else if (candidateCost(events) != cost) {
|
|
12999
|
+
throw new Error(`cost of block ${i} changed`);
|
|
13000
|
+
}
|
|
13001
|
+
candidateDecls.set(decl, events);
|
|
13002
|
+
}
|
|
13003
|
+
});
|
|
13004
|
+
});
|
|
13005
|
+
if (candidateDecls.size) {
|
|
13006
|
+
return candidateDecls;
|
|
13007
|
+
}
|
|
13008
|
+
return null;
|
|
13009
|
+
}
|
|
13010
|
+
/*
|
|
13011
|
+
* Determine the cost of fixing a def under a statement.
|
|
13012
|
+
*
|
|
13013
|
+
* eg:
|
|
13014
|
+
*
|
|
13015
|
+
* if (foo()) {
|
|
13016
|
+
* bar(X.y);
|
|
13017
|
+
* } else {
|
|
13018
|
+
* baz(X.y);
|
|
13019
|
+
* }
|
|
13020
|
+
*
|
|
13021
|
+
* Here, we could pull out X.y as a local, but if foo might modify
|
|
13022
|
+
* X.y, we have nowhere to insert the temporary. But we can rewrite
|
|
13023
|
+
* it as:
|
|
13024
|
+
*
|
|
13025
|
+
* var tmp = foo();
|
|
13026
|
+
* if (tmp) {
|
|
13027
|
+
* bar(X.y);
|
|
13028
|
+
* } else {
|
|
13029
|
+
* baz(X.y);
|
|
13030
|
+
* }
|
|
13031
|
+
*
|
|
13032
|
+
* and now we can insert a temporary before the if, but it costs
|
|
13033
|
+
* 4 bytes to do so.
|
|
13034
|
+
*
|
|
13035
|
+
* We can do the same for switch statements unless (ugh!)
|
|
13036
|
+
* the cases might modify the decl too.
|
|
13037
|
+
*
|
|
13038
|
+
* eg
|
|
13039
|
+
*
|
|
13040
|
+
* switch (foo()) {
|
|
13041
|
+
* case bar(): ...
|
|
13042
|
+
* }
|
|
13043
|
+
*
|
|
13044
|
+
*/
|
|
13045
|
+
function _isFixableStmt(node) {
|
|
13046
|
+
switch (node.type) {
|
|
13047
|
+
case "IfStatement":
|
|
13048
|
+
return 4;
|
|
13049
|
+
case "SwitchStatement":
|
|
13050
|
+
if (node.cases.every((c) => !c.test ||
|
|
13051
|
+
c.test.type === "Literal" ||
|
|
13052
|
+
c.test.type === "Identifier" ||
|
|
13053
|
+
c.test.type === "InstanceOfCase" ||
|
|
13054
|
+
(c.test.type === "UnaryExpression" && c.test.operator === ":"))) {
|
|
13055
|
+
return 4;
|
|
13056
|
+
}
|
|
13057
|
+
break;
|
|
13058
|
+
}
|
|
13059
|
+
return false;
|
|
13060
|
+
}
|
|
13061
|
+
function applyReplacements(func, nodeMap, declMap) {
|
|
13062
|
+
const ident = (name, node) => {
|
|
13063
|
+
return withLoc({ type: "Identifier", name }, node);
|
|
13064
|
+
};
|
|
13065
|
+
const pendingMap = new Map();
|
|
13066
|
+
const stmtStack = [func];
|
|
13067
|
+
traverseAst(func, (node) => {
|
|
13068
|
+
if (isStatement(node)) {
|
|
13069
|
+
stmtStack.push(node);
|
|
13070
|
+
}
|
|
13071
|
+
}, (node) => {
|
|
13072
|
+
const stmt = stmtStack[stmtStack.length - 1];
|
|
13073
|
+
if (stmt === node)
|
|
13074
|
+
stmtStack.pop();
|
|
13075
|
+
const events = nodeMap.get(node);
|
|
13076
|
+
if (events) {
|
|
13077
|
+
if (events.length === 1) {
|
|
13078
|
+
if (events[0].type === "ref") {
|
|
13079
|
+
if (node.type !== "Identifier" &&
|
|
13080
|
+
node.type !== "MemberExpression" &&
|
|
13081
|
+
node.type !== "Literal") {
|
|
13082
|
+
throw new Error(`Ref found, but wrong type of node: ${node.type}`);
|
|
13083
|
+
}
|
|
13084
|
+
const name = declMap.get(events[0].decl);
|
|
13085
|
+
if (!name) {
|
|
13086
|
+
throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(node)}"`);
|
|
13087
|
+
}
|
|
13088
|
+
return ident(name, node);
|
|
13089
|
+
}
|
|
13090
|
+
else if (events[0].type === "def") {
|
|
13091
|
+
if (node.type !== "AssignmentExpression" &&
|
|
13092
|
+
node.type !== "UpdateExpression") {
|
|
13093
|
+
throw new Error(`Def found, but wrong type of node: ${node.type}`);
|
|
13094
|
+
}
|
|
13095
|
+
const target = node.type === "AssignmentExpression"
|
|
13096
|
+
? node.left
|
|
13097
|
+
: node.argument;
|
|
13098
|
+
const name = declMap.get(events[0].decl);
|
|
13099
|
+
if (!name) {
|
|
13100
|
+
throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(target)}"`);
|
|
13101
|
+
}
|
|
13102
|
+
const id = ident(name, target);
|
|
13103
|
+
const assign = withLoc({
|
|
13104
|
+
type: "AssignmentExpression",
|
|
13105
|
+
left: target,
|
|
13106
|
+
right: { ...id },
|
|
13107
|
+
operator: "=",
|
|
13108
|
+
}, node);
|
|
13109
|
+
if (node.type === "AssignmentExpression") {
|
|
13110
|
+
node.left = id;
|
|
13111
|
+
}
|
|
13112
|
+
else {
|
|
13113
|
+
node.argument = id;
|
|
13114
|
+
}
|
|
13115
|
+
return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
|
|
13116
|
+
}
|
|
13117
|
+
}
|
|
13118
|
+
events.forEach((event) => {
|
|
13119
|
+
if (event.type !== "mod") {
|
|
13120
|
+
throw new Error(`Unexpected ${event.type} found amongst multiple events`);
|
|
13121
|
+
}
|
|
13122
|
+
if (!event.decl) {
|
|
13123
|
+
throw new Error(`Unexpected null decl on mod event`);
|
|
13124
|
+
}
|
|
13125
|
+
let pending = pendingMap.get(stmt);
|
|
13126
|
+
if (!pending) {
|
|
13127
|
+
pendingMap.set(stmt, (pending = new Set()));
|
|
13128
|
+
}
|
|
13129
|
+
pending.add(event);
|
|
13130
|
+
});
|
|
13131
|
+
}
|
|
13132
|
+
const pending = pendingMap.get(node);
|
|
13133
|
+
if (node.type === "SequenceExpression") {
|
|
13134
|
+
if (pending) {
|
|
13135
|
+
throw new Error(`Unexpected pending list at SequenceExpression`);
|
|
13136
|
+
}
|
|
13137
|
+
for (let i = node.expressions.length; i--;) {
|
|
13138
|
+
const ni = node.expressions[i];
|
|
13139
|
+
if (ni.type === "SequenceExpression") {
|
|
13140
|
+
node.expressions.splice(i, 1, ...ni.expressions);
|
|
13141
|
+
}
|
|
13142
|
+
}
|
|
13143
|
+
}
|
|
13144
|
+
const applyPending = (results, locNode) => {
|
|
13145
|
+
const target = results.length === 1 && results[0].type === "BlockStatement"
|
|
13146
|
+
? results[0]
|
|
13147
|
+
: null;
|
|
13148
|
+
pendingMap.delete(node);
|
|
13149
|
+
pending.forEach((event) => {
|
|
13150
|
+
const decl = event.decl;
|
|
13151
|
+
const name = declMap.get(decl);
|
|
13152
|
+
if (!name) {
|
|
13153
|
+
throw new Error(`No replacement found for "${declFullName(decl)}"`);
|
|
13154
|
+
}
|
|
13155
|
+
if (!event.id) {
|
|
13156
|
+
throw new Error(`Missing id for mod event for "${declFullName(decl)}"`);
|
|
13157
|
+
}
|
|
13158
|
+
const rhs = withLocDeep(event.id, locNode, locNode);
|
|
13159
|
+
rhs.end = rhs.start;
|
|
13160
|
+
if (rhs.loc) {
|
|
13161
|
+
rhs.loc.end = rhs.loc.start;
|
|
13162
|
+
}
|
|
13163
|
+
const insertion = withLoc({
|
|
13164
|
+
type: "ExpressionStatement",
|
|
13165
|
+
expression: withLoc({
|
|
13166
|
+
type: "AssignmentExpression",
|
|
13167
|
+
left: ident(name, rhs),
|
|
13168
|
+
right: rhs,
|
|
13169
|
+
operator: "=",
|
|
13170
|
+
}, rhs),
|
|
13171
|
+
}, rhs);
|
|
13172
|
+
if (event.type === "mod" && event.before) {
|
|
13173
|
+
if (target) {
|
|
13174
|
+
target.body.unshift(insertion);
|
|
13175
|
+
}
|
|
13176
|
+
else {
|
|
13177
|
+
results.unshift(insertion);
|
|
13178
|
+
}
|
|
13179
|
+
}
|
|
13180
|
+
else {
|
|
13181
|
+
results.push(insertion);
|
|
13182
|
+
}
|
|
13183
|
+
});
|
|
13184
|
+
return results.length === 1 ? null : results;
|
|
13185
|
+
};
|
|
13186
|
+
if (node.type === "ExpressionStatement" &&
|
|
13187
|
+
node.expression.type === "SequenceExpression") {
|
|
13188
|
+
const results = [];
|
|
13189
|
+
node.expression.expressions.forEach((expression) => {
|
|
13190
|
+
results.push({ ...node, expression });
|
|
13191
|
+
});
|
|
13192
|
+
if (!pending) {
|
|
13193
|
+
return results;
|
|
13194
|
+
}
|
|
13195
|
+
return applyPending(results, node);
|
|
13196
|
+
}
|
|
13197
|
+
if (pending) {
|
|
13198
|
+
return applyPending([node], node);
|
|
13199
|
+
}
|
|
13200
|
+
return null;
|
|
13201
|
+
});
|
|
13202
|
+
}
|
|
13203
|
+
|
|
13204
|
+
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
13205
|
+
|
|
13206
|
+
function visitReferences(state, ast, name, defn, callback) {
|
|
13207
|
+
const checkResults = ([name, results], node) => {
|
|
13208
|
+
if (name && results) {
|
|
13209
|
+
if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
|
|
13210
|
+
if (callback(node, results, false) === false) {
|
|
13211
|
+
return [];
|
|
13212
|
+
}
|
|
13213
|
+
}
|
|
13214
|
+
}
|
|
13215
|
+
else if (defn === false) {
|
|
13216
|
+
if (callback(node, [], results === null) === false) {
|
|
13217
|
+
return [];
|
|
13218
|
+
}
|
|
13219
|
+
}
|
|
13220
|
+
return null;
|
|
13221
|
+
};
|
|
13222
|
+
state.pre = (node) => {
|
|
13223
|
+
switch (node.type) {
|
|
13224
|
+
case "AttributeList":
|
|
13225
|
+
return [];
|
|
13226
|
+
case "UnaryExpression":
|
|
13227
|
+
// a bare symbol isn't a reference
|
|
13228
|
+
if (node.operator === ":")
|
|
13229
|
+
return [];
|
|
13230
|
+
break;
|
|
13231
|
+
case "BinaryExpression":
|
|
13232
|
+
/*
|
|
13233
|
+
* `expr has :symbol` can be treated as a reference
|
|
13234
|
+
* to expr.symbol.
|
|
13235
|
+
*/
|
|
13236
|
+
if (node.operator === "has") {
|
|
13237
|
+
if (node.right.type === "UnaryExpression" &&
|
|
13238
|
+
node.right.operator === ":") {
|
|
13239
|
+
if (!name || node.right.argument.name === name) {
|
|
13240
|
+
return checkResults(state.lookup({
|
|
13241
|
+
type: "MemberExpression",
|
|
13242
|
+
object: node.left,
|
|
13243
|
+
property: node.right.argument,
|
|
13244
|
+
computed: false,
|
|
13245
|
+
}), node.right.argument);
|
|
13246
|
+
}
|
|
13247
|
+
}
|
|
13248
|
+
}
|
|
13249
|
+
break;
|
|
13250
|
+
case "CallExpression":
|
|
13251
|
+
// A call expression whose callee is an identifier is looked
|
|
13252
|
+
// up as a non-local. ie even if there's a same named local,
|
|
13253
|
+
// it will be ignored, and the lookup will start as if the
|
|
13254
|
+
// call had been written self.foo() rather than foo().
|
|
13255
|
+
if (node.callee.type === "Identifier") {
|
|
13256
|
+
if (!name || node.callee.name === name) {
|
|
13257
|
+
/* ignore return value */
|
|
13258
|
+
checkResults(state.lookupNonlocal(node.callee), node.callee);
|
|
13259
|
+
}
|
|
13260
|
+
return ["arguments"];
|
|
13261
|
+
}
|
|
13262
|
+
break;
|
|
13263
|
+
case "Identifier":
|
|
13264
|
+
if (!name || node.name === name) {
|
|
13265
|
+
return checkResults(state.lookup(node), node);
|
|
13266
|
+
}
|
|
13267
|
+
break;
|
|
13268
|
+
case "MemberExpression":
|
|
13269
|
+
if (!node.computed && node.property.type === "Identifier") {
|
|
13270
|
+
if (!name || node.property.name === name) {
|
|
13271
|
+
return checkResults(state.lookup(node), node) || ["object"];
|
|
13272
|
+
}
|
|
13273
|
+
return ["object"];
|
|
13274
|
+
}
|
|
13275
|
+
break;
|
|
13276
|
+
case "MethodDefinition": {
|
|
13277
|
+
if (!state.inType) {
|
|
13278
|
+
throw new Error("Method definition outside of type!");
|
|
13279
|
+
}
|
|
13280
|
+
if (node.params) {
|
|
13281
|
+
node.params.forEach((param) => {
|
|
13282
|
+
if (param.type == "BinaryExpression") {
|
|
13283
|
+
state.traverse(param.right);
|
|
13284
|
+
state.inType = true;
|
|
11606
13285
|
}
|
|
11607
13286
|
});
|
|
11608
13287
|
}
|
|
@@ -11624,6 +13303,7 @@ function visitReferences(state, ast, name, defn, callback) {
|
|
|
11624
13303
|
|
|
11625
13304
|
|
|
11626
13305
|
|
|
13306
|
+
|
|
11627
13307
|
function collectClassInfo(state) {
|
|
11628
13308
|
const toybox = state.stack[0].decls["Toybox"][0];
|
|
11629
13309
|
const lang = toybox.decls["Lang"][0];
|
|
@@ -11924,7 +13604,50 @@ function getNodeValue(node) {
|
|
|
11924
13604
|
}
|
|
11925
13605
|
return [node, type];
|
|
11926
13606
|
}
|
|
11927
|
-
function
|
|
13607
|
+
function fullTypeName(state, tsp) {
|
|
13608
|
+
if (typeof tsp.name === "string") {
|
|
13609
|
+
return tsp.name;
|
|
13610
|
+
}
|
|
13611
|
+
const [, results] = state.lookupType(tsp.name);
|
|
13612
|
+
if (results && results.length === 1 && results[0].results.length === 1) {
|
|
13613
|
+
const result = results[0].results[0];
|
|
13614
|
+
if ((0,external_api_cjs_namespaceObject.isStateNode)(result)) {
|
|
13615
|
+
return result.fullName;
|
|
13616
|
+
}
|
|
13617
|
+
}
|
|
13618
|
+
return null;
|
|
13619
|
+
}
|
|
13620
|
+
function isBooleanExpression(state, node) {
|
|
13621
|
+
switch (node.type) {
|
|
13622
|
+
case "Literal":
|
|
13623
|
+
return typeof node.value === "boolean";
|
|
13624
|
+
case "BinaryExpression":
|
|
13625
|
+
switch (node.operator) {
|
|
13626
|
+
case "==":
|
|
13627
|
+
case "!=":
|
|
13628
|
+
case "<=":
|
|
13629
|
+
case ">=":
|
|
13630
|
+
case "<":
|
|
13631
|
+
case ">":
|
|
13632
|
+
return true;
|
|
13633
|
+
case "as":
|
|
13634
|
+
return node.right.ts.length === 1 &&
|
|
13635
|
+
node.right.ts[0].type === "TypeSpecPart" &&
|
|
13636
|
+
node.right.ts[0].name &&
|
|
13637
|
+
fullTypeName(state, node.right.ts[0]) === "$.Toybox.Lang.Boolean"
|
|
13638
|
+
? true
|
|
13639
|
+
: false;
|
|
13640
|
+
}
|
|
13641
|
+
return false;
|
|
13642
|
+
case "LogicalExpression":
|
|
13643
|
+
return (isBooleanExpression(state, node.left) &&
|
|
13644
|
+
isBooleanExpression(state, node.right));
|
|
13645
|
+
case "UnaryExpression":
|
|
13646
|
+
return node.operator === "!" && isBooleanExpression(state, node.argument);
|
|
13647
|
+
}
|
|
13648
|
+
return false;
|
|
13649
|
+
}
|
|
13650
|
+
function optimizeNode(state, node) {
|
|
11928
13651
|
switch (node.type) {
|
|
11929
13652
|
case "UnaryExpression": {
|
|
11930
13653
|
const [arg, type] = getNodeValue(node.argument);
|
|
@@ -11976,8 +13699,18 @@ function optimizeNode(node) {
|
|
|
11976
13699
|
"%": (left, right) => left % right,
|
|
11977
13700
|
"&": (left, right, type) => type === "Number" ? left & right : null,
|
|
11978
13701
|
"|": (left, right, type) => type === "Number" ? left | right : null,
|
|
13702
|
+
"^": (left, right, type) => type === "Number" ? left ^ right : null,
|
|
11979
13703
|
"<<": (left, right, type) => type === "Number" ? left << right : null,
|
|
11980
13704
|
">>": (left, right, type) => type === "Number" ? left >> right : null,
|
|
13705
|
+
"==": (left, right) => left == right,
|
|
13706
|
+
"!=": (left, right) => left != right,
|
|
13707
|
+
"<=": (left, right) => left <= right,
|
|
13708
|
+
">=": (left, right) => left >= right,
|
|
13709
|
+
"<": (left, right) => left < right,
|
|
13710
|
+
">": (left, right) => left > right,
|
|
13711
|
+
as: null,
|
|
13712
|
+
instanceof: null,
|
|
13713
|
+
has: null,
|
|
11981
13714
|
};
|
|
11982
13715
|
const op = operators[node.operator];
|
|
11983
13716
|
if (op) {
|
|
@@ -11985,11 +13718,17 @@ function optimizeNode(node) {
|
|
|
11985
13718
|
const [right, right_type] = getNodeValue(node.right);
|
|
11986
13719
|
if (!left || !right)
|
|
11987
13720
|
break;
|
|
13721
|
+
let value = null;
|
|
11988
13722
|
if (left_type != right_type ||
|
|
11989
13723
|
(left_type != "Number" && left_type != "Long")) {
|
|
11990
|
-
|
|
13724
|
+
if (node.operator !== "==" && node.operator !== "!=") {
|
|
13725
|
+
break;
|
|
13726
|
+
}
|
|
13727
|
+
value = operators[node.operator](left.value, right.value);
|
|
13728
|
+
}
|
|
13729
|
+
else {
|
|
13730
|
+
value = op(left.value, right.value, left_type);
|
|
11991
13731
|
}
|
|
11992
|
-
const value = op(left.value, right.value, left_type);
|
|
11993
13732
|
if (value === null)
|
|
11994
13733
|
break;
|
|
11995
13734
|
return {
|
|
@@ -12000,15 +13739,47 @@ function optimizeNode(node) {
|
|
|
12000
13739
|
}
|
|
12001
13740
|
break;
|
|
12002
13741
|
}
|
|
13742
|
+
case "LogicalExpression": {
|
|
13743
|
+
const [left, left_type] = getNodeValue(node.left);
|
|
13744
|
+
if (!left)
|
|
13745
|
+
break;
|
|
13746
|
+
const falsy = left.value === false ||
|
|
13747
|
+
left.value === null ||
|
|
13748
|
+
(left.value === 0 && (left_type === "Number" || left_type === "Long"));
|
|
13749
|
+
if (falsy === (node.operator === "&&")) {
|
|
13750
|
+
return left;
|
|
13751
|
+
}
|
|
13752
|
+
if (left_type !== "Boolean" &&
|
|
13753
|
+
left_type !== "Number" &&
|
|
13754
|
+
left_type !== "Long") {
|
|
13755
|
+
break;
|
|
13756
|
+
}
|
|
13757
|
+
const [right, right_type] = getNodeValue(node.right);
|
|
13758
|
+
if (right && right_type === left_type) {
|
|
13759
|
+
if (left_type === "Boolean" || node.operator === "||") {
|
|
13760
|
+
return right;
|
|
13761
|
+
}
|
|
13762
|
+
if (node.operator !== "&&") {
|
|
13763
|
+
throw new Error(`Unexpected operator "${node.operator}"`);
|
|
13764
|
+
}
|
|
13765
|
+
return { ...node, type: "BinaryExpression", operator: "&" };
|
|
13766
|
+
}
|
|
13767
|
+
if (left_type === "Boolean") {
|
|
13768
|
+
if (isBooleanExpression(state, node.right)) {
|
|
13769
|
+
return node.right;
|
|
13770
|
+
}
|
|
13771
|
+
}
|
|
13772
|
+
break;
|
|
13773
|
+
}
|
|
12003
13774
|
case "FunctionDeclaration":
|
|
12004
|
-
if (node.body && evaluateFunction(node, null) !== false) {
|
|
13775
|
+
if (node.body && evaluateFunction(state, node, null) !== false) {
|
|
12005
13776
|
node.optimizable = true;
|
|
12006
13777
|
}
|
|
12007
13778
|
break;
|
|
12008
13779
|
}
|
|
12009
13780
|
return null;
|
|
12010
13781
|
}
|
|
12011
|
-
function evaluateFunction(func, args) {
|
|
13782
|
+
function evaluateFunction(state, func, args) {
|
|
12012
13783
|
if (!func.body || (args && args.length != func.params.length)) {
|
|
12013
13784
|
return false;
|
|
12014
13785
|
}
|
|
@@ -12047,7 +13818,7 @@ function evaluateFunction(func, args) {
|
|
|
12047
13818
|
}
|
|
12048
13819
|
// fall through;
|
|
12049
13820
|
default: {
|
|
12050
|
-
const repl = optimizeNode(node);
|
|
13821
|
+
const repl = optimizeNode(state, node);
|
|
12051
13822
|
if (repl && repl.type === "Literal")
|
|
12052
13823
|
return repl;
|
|
12053
13824
|
throw new Error("Didn't optimize");
|
|
@@ -12107,10 +13878,19 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
12107
13878
|
if (!objects) {
|
|
12108
13879
|
return false;
|
|
12109
13880
|
}
|
|
12110
|
-
|
|
13881
|
+
let obj = getLiteralFromDecls(objects);
|
|
12111
13882
|
if (!obj) {
|
|
12112
13883
|
return false;
|
|
12113
13884
|
}
|
|
13885
|
+
while (obj.type === "BinaryExpression") {
|
|
13886
|
+
if (obj.left.type === "BinaryExpression" && obj.left.operator === "as") {
|
|
13887
|
+
obj = { ...obj, left: obj.left.left };
|
|
13888
|
+
}
|
|
13889
|
+
else {
|
|
13890
|
+
obj = { ...obj, left: { ...obj.left } };
|
|
13891
|
+
break;
|
|
13892
|
+
}
|
|
13893
|
+
}
|
|
12114
13894
|
inPlaceReplacement(node, obj);
|
|
12115
13895
|
return true;
|
|
12116
13896
|
};
|
|
@@ -12328,14 +14108,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
12328
14108
|
return null;
|
|
12329
14109
|
};
|
|
12330
14110
|
state.post = (node) => {
|
|
12331
|
-
|
|
14111
|
+
const locals = topLocals();
|
|
14112
|
+
if (locals.node === node) {
|
|
12332
14113
|
state.localsStack.pop();
|
|
12333
14114
|
}
|
|
12334
|
-
const opt = optimizeNode(node);
|
|
14115
|
+
const opt = optimizeNode(state, node);
|
|
12335
14116
|
if (opt) {
|
|
12336
14117
|
return replace(opt, node);
|
|
12337
14118
|
}
|
|
12338
14119
|
switch (node.type) {
|
|
14120
|
+
case "BlockStatement":
|
|
14121
|
+
if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
|
|
14122
|
+
node.body.splice(0, 1, ...node.body[0].body);
|
|
14123
|
+
}
|
|
14124
|
+
break;
|
|
12339
14125
|
case "ConditionalExpression":
|
|
12340
14126
|
case "IfStatement":
|
|
12341
14127
|
if (node.test.type === "Literal" &&
|
|
@@ -12345,6 +14131,12 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
12345
14131
|
return false;
|
|
12346
14132
|
return replace(rep, rep);
|
|
12347
14133
|
}
|
|
14134
|
+
else if (node.type === "IfStatement" &&
|
|
14135
|
+
node.alternate &&
|
|
14136
|
+
node.alternate.type === "BlockStatement" &&
|
|
14137
|
+
!node.alternate.body.length) {
|
|
14138
|
+
delete node.alternate;
|
|
14139
|
+
}
|
|
12348
14140
|
break;
|
|
12349
14141
|
case "WhileStatement":
|
|
12350
14142
|
if (node.test.type === "Literal" && node.test.value === false) {
|
|
@@ -12372,6 +14164,48 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
12372
14164
|
return { type: "Literal", value: null, raw: "null" };
|
|
12373
14165
|
}
|
|
12374
14166
|
break;
|
|
14167
|
+
case "VariableDeclaration": {
|
|
14168
|
+
const locals = topLocals();
|
|
14169
|
+
if (locals.map &&
|
|
14170
|
+
locals.node &&
|
|
14171
|
+
locals.node.type === "BlockStatement") {
|
|
14172
|
+
let results;
|
|
14173
|
+
const declarations = node.declarations;
|
|
14174
|
+
let i = 0;
|
|
14175
|
+
let j = 0;
|
|
14176
|
+
while (i < node.declarations.length) {
|
|
14177
|
+
const decl = declarations[i++];
|
|
14178
|
+
if (decl.init && decl.init.type === "CallExpression") {
|
|
14179
|
+
const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
|
|
14180
|
+
if (!inlined)
|
|
14181
|
+
continue;
|
|
14182
|
+
if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
|
|
14183
|
+
throw new Error("Unexpected inlined result");
|
|
14184
|
+
}
|
|
14185
|
+
if (!results) {
|
|
14186
|
+
results = [];
|
|
14187
|
+
}
|
|
14188
|
+
delete decl.init;
|
|
14189
|
+
results.push(withLoc({
|
|
14190
|
+
...node,
|
|
14191
|
+
declarations: declarations.slice(j, i),
|
|
14192
|
+
}, j ? declarations[j] : null, decl.id));
|
|
14193
|
+
results.push(inlined);
|
|
14194
|
+
j = i;
|
|
14195
|
+
}
|
|
14196
|
+
}
|
|
14197
|
+
if (results) {
|
|
14198
|
+
if (j < i) {
|
|
14199
|
+
results.push({
|
|
14200
|
+
...node,
|
|
14201
|
+
declarations: declarations.slice(j, i),
|
|
14202
|
+
});
|
|
14203
|
+
}
|
|
14204
|
+
return results;
|
|
14205
|
+
}
|
|
14206
|
+
}
|
|
14207
|
+
break;
|
|
14208
|
+
}
|
|
12375
14209
|
case "ExpressionStatement":
|
|
12376
14210
|
if (node.expression.type === "CallExpression") {
|
|
12377
14211
|
return replace(optimizeCall(state, node.expression, node), node.expression);
|
|
@@ -12416,6 +14250,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
12416
14250
|
});
|
|
12417
14251
|
delete state.pre;
|
|
12418
14252
|
delete state.post;
|
|
14253
|
+
state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
|
|
12419
14254
|
const cleanup = (node) => {
|
|
12420
14255
|
switch (node.type) {
|
|
12421
14256
|
case "ThisExpression":
|
|
@@ -12541,7 +14376,7 @@ function optimizeCall(state, node, context) {
|
|
|
12541
14376
|
callee.optimizable &&
|
|
12542
14377
|
!callee.hasOverride &&
|
|
12543
14378
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
12544
|
-
const ret = evaluateFunction(callee, node.arguments);
|
|
14379
|
+
const ret = evaluateFunction(state, callee, node.arguments);
|
|
12545
14380
|
if (ret) {
|
|
12546
14381
|
return ret;
|
|
12547
14382
|
}
|
|
@@ -12602,7 +14437,7 @@ function pragmaChecker(ast, diagnostics) {
|
|
|
12602
14437
|
if (quote == '"') {
|
|
12603
14438
|
return haystack.includes(needle);
|
|
12604
14439
|
}
|
|
12605
|
-
const re = new RegExp(needle);
|
|
14440
|
+
const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
|
|
12606
14441
|
return re.test(haystack);
|
|
12607
14442
|
};
|
|
12608
14443
|
next();
|
|
@@ -12612,8 +14447,10 @@ function pragmaChecker(ast, diagnostics) {
|
|
|
12612
14447
|
if (node.start && node.start >= (comment.end || Infinity)) {
|
|
12613
14448
|
const { kind, quote, needle } = matchers.shift();
|
|
12614
14449
|
if (kind === "match") {
|
|
12615
|
-
|
|
12616
|
-
|
|
14450
|
+
const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " ");
|
|
14451
|
+
if (!matcher(quote, needle, haystack)) {
|
|
14452
|
+
matcher(quote, needle, haystack);
|
|
14453
|
+
throw new Error(`Didn't find '${needle}' in '${haystack}' at ${comment.loc.source}:${comment.loc.start.line}`);
|
|
12617
14454
|
}
|
|
12618
14455
|
}
|
|
12619
14456
|
else if (kind === "expect") {
|
|
@@ -13054,6 +14891,7 @@ const configOptionsToCheck = [
|
|
|
13054
14891
|
"ignoredAnnotations",
|
|
13055
14892
|
"ignoredSourcePaths",
|
|
13056
14893
|
"checkInvalidSymbols",
|
|
14894
|
+
"sizeBasedPRE",
|
|
13057
14895
|
];
|
|
13058
14896
|
/**
|
|
13059
14897
|
* @param {BuildConfig} config
|
|
@@ -13105,7 +14943,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
|
|
|
13105
14943
|
// the oldest optimized file, we don't need to regenerate
|
|
13106
14944
|
const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
|
|
13107
14945
|
const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
|
|
13108
|
-
if (source_time < opt_time &&
|
|
14946
|
+
if (source_time < opt_time && 1656860602542 < opt_time) {
|
|
13109
14947
|
return { hasTests, diagnostics: prevDiagnostics };
|
|
13110
14948
|
}
|
|
13111
14949
|
}
|