@markw65/monkeyc-optimizer 1.0.45 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/build/api.cjs +448 -4794
- package/build/optimizer.cjs +3243 -690
- package/build/sdk-util.cjs +22 -2
- package/build/src/api.d.ts +5 -3
- package/build/src/ast.d.ts +1 -0
- package/build/src/control-flow.d.ts +1 -1
- package/build/src/data-flow.d.ts +1 -1
- package/build/src/inliner.d.ts +1 -2
- package/build/src/mc-rewrite.d.ts +1 -2
- package/build/src/optimizer-types.d.ts +11 -2
- package/build/src/type-flow.d.ts +0 -1
- package/build/src/worker-pool.d.ts +1 -1
- package/build/src/worker-task.d.ts +49 -18
- package/build/src/worker-thread.d.ts +1 -0
- package/package.json +7 -2
- package/build/src/interp-binary.d.ts +0 -4
- package/build/src/interp-call.d.ts +0 -3
- package/build/src/interp.d.ts +0 -23
- package/build/src/mc-types.d.ts +0 -166
package/build/optimizer.cjs
CHANGED
|
@@ -4723,8 +4723,6 @@ function mayThrow(node) {
|
|
|
4723
4723
|
switch (node.type) {
|
|
4724
4724
|
case "BinaryExpression":
|
|
4725
4725
|
case "CallExpression":
|
|
4726
|
-
case "ConditionalExpression":
|
|
4727
|
-
case "LogicalExpression":
|
|
4728
4726
|
case "NewExpression":
|
|
4729
4727
|
case "ThrowStatement":
|
|
4730
4728
|
case "UnaryExpression":
|
|
@@ -4869,6 +4867,28 @@ function makeScopedName(dotted, l) {
|
|
|
4869
4867
|
throw new Error("Failed to make a ScopedName");
|
|
4870
4868
|
return result;
|
|
4871
4869
|
}
|
|
4870
|
+
function ast_getLiteralNode(node) {
|
|
4871
|
+
if (node == null)
|
|
4872
|
+
return null;
|
|
4873
|
+
if (node.type == "Literal")
|
|
4874
|
+
return node;
|
|
4875
|
+
if (node.type == "BinaryExpression" && node.operator == "as") {
|
|
4876
|
+
return ast_getLiteralNode(node.left) && node;
|
|
4877
|
+
}
|
|
4878
|
+
if (node.type == "UnaryExpression") {
|
|
4879
|
+
if (node.argument.type != "Literal")
|
|
4880
|
+
return null;
|
|
4881
|
+
switch (node.operator) {
|
|
4882
|
+
case "-": {
|
|
4883
|
+
const [arg, type] = getNodeValue(node.argument);
|
|
4884
|
+
if (type === "Number" || type === "Long") {
|
|
4885
|
+
return { ...arg, value: -arg.value, raw: `-${arg.raw}` };
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4890
|
+
return null;
|
|
4891
|
+
}
|
|
4872
4892
|
|
|
4873
4893
|
;// CONCATENATED MODULE: ./src/function-info.ts
|
|
4874
4894
|
|
|
@@ -5602,31 +5622,9 @@ function unused(state, expression, top) {
|
|
|
5602
5622
|
}
|
|
5603
5623
|
return top ? null : [estmt(expression)];
|
|
5604
5624
|
}
|
|
5605
|
-
function diagnostic(state, loc, message, type = "INFO") {
|
|
5606
|
-
if (!loc || !loc.source)
|
|
5607
|
-
return;
|
|
5608
|
-
const source = loc.source;
|
|
5609
|
-
if (!state.diagnostics)
|
|
5610
|
-
state.diagnostics = {};
|
|
5611
|
-
if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
|
|
5612
|
-
if (!message)
|
|
5613
|
-
return;
|
|
5614
|
-
state.diagnostics[source] = [];
|
|
5615
|
-
}
|
|
5616
|
-
const diags = state.diagnostics[source];
|
|
5617
|
-
let index = diags.findIndex((item) => item.loc === loc);
|
|
5618
|
-
if (message) {
|
|
5619
|
-
if (index < 0)
|
|
5620
|
-
index = diags.length;
|
|
5621
|
-
diags[index] = { type, loc, message };
|
|
5622
|
-
}
|
|
5623
|
-
else if (index >= 0) {
|
|
5624
|
-
diags.splice(index, 1);
|
|
5625
|
-
}
|
|
5626
|
-
}
|
|
5627
5625
|
function inlineDiagnostic(state, func, call, message) {
|
|
5628
5626
|
if (inlineRequested(state, func)) {
|
|
5629
|
-
diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
|
|
5627
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
|
|
5630
5628
|
}
|
|
5631
5629
|
}
|
|
5632
5630
|
function inlineWithArgs(state, func, call, context) {
|
|
@@ -5651,8 +5649,9 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
5651
5649
|
});
|
|
5652
5650
|
if (retStmtCount > 1) {
|
|
5653
5651
|
inlineDiagnostic(state, func, call, "Function had more than one return statement");
|
|
5652
|
+
return null;
|
|
5654
5653
|
}
|
|
5655
|
-
|
|
5654
|
+
if ((context.type === "AssignmentExpression" ||
|
|
5656
5655
|
context.type === "VariableDeclarator" ||
|
|
5657
5656
|
context.type === "IfStatement") &&
|
|
5658
5657
|
retStmtCount !== 1) {
|
|
@@ -5684,7 +5683,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
5684
5683
|
if (!processInlineBody(state, func, call, body, params)) {
|
|
5685
5684
|
return null;
|
|
5686
5685
|
}
|
|
5687
|
-
diagnostic(state, call.loc, null);
|
|
5686
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, call.loc, null);
|
|
5688
5687
|
if (context.type !== "ReturnStatement" && retStmtCount) {
|
|
5689
5688
|
const [last, block] = lastStmt(body);
|
|
5690
5689
|
if (last.type != "ReturnStatement") {
|
|
@@ -5921,7 +5920,6 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
5921
5920
|
;// CONCATENATED MODULE: ./src/pragma-checker.ts
|
|
5922
5921
|
|
|
5923
5922
|
|
|
5924
|
-
|
|
5925
5923
|
function pragmaChecker(state, ast, diagnostics) {
|
|
5926
5924
|
const comments = ast.comments;
|
|
5927
5925
|
if (!comments)
|
|
@@ -5963,7 +5961,7 @@ function pragmaChecker(state, ast, diagnostics) {
|
|
|
5963
5961
|
break;
|
|
5964
5962
|
}
|
|
5965
5963
|
}
|
|
5966
|
-
diagnostic(state, comment.loc, `Build pragma '${comment.value}' is invalid`, "ERROR");
|
|
5964
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, comment.loc, `Build pragma '${comment.value}' is invalid`, "ERROR");
|
|
5967
5965
|
}
|
|
5968
5966
|
};
|
|
5969
5967
|
const matcher = (quote, needle, haystack) => {
|
|
@@ -5987,7 +5985,7 @@ function pragmaChecker(state, ast, diagnostics) {
|
|
|
5987
5985
|
const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " ");
|
|
5988
5986
|
if (!matcher(quote, needle, haystack)) {
|
|
5989
5987
|
matcher(quote, needle, haystack);
|
|
5990
|
-
diagnostic(state, comment.loc, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
|
|
5988
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, comment.loc, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
|
|
5991
5989
|
}
|
|
5992
5990
|
}
|
|
5993
5991
|
else if (kind === "expect") {
|
|
@@ -6029,7 +6027,7 @@ function pragmaChecker(state, ast, diagnostics) {
|
|
|
6029
6027
|
}
|
|
6030
6028
|
}
|
|
6031
6029
|
if (!found) {
|
|
6032
|
-
diagnostic(state, comment.loc, `Missing error message '${needle}`, "ERROR");
|
|
6030
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, comment.loc, `Missing error message '${needle}`, "ERROR");
|
|
6033
6031
|
}
|
|
6034
6032
|
}
|
|
6035
6033
|
if (matchers.length) {
|
|
@@ -6128,6 +6126,7 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6128
6126
|
const ret = localState.curBlock;
|
|
6129
6127
|
state.stack = [...func.stack];
|
|
6130
6128
|
const stmtStack = [func.node];
|
|
6129
|
+
const testStack = [{ node: func.node }];
|
|
6131
6130
|
let tryActive = 0;
|
|
6132
6131
|
state.pre = (node) => {
|
|
6133
6132
|
if (state.inType || localState.unreachable) {
|
|
@@ -6137,6 +6136,10 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6137
6136
|
(isStatement(node) || isExpression(node))) {
|
|
6138
6137
|
localState.curBlock.node = node;
|
|
6139
6138
|
}
|
|
6139
|
+
let topTest = testStack[testStack.length - 1];
|
|
6140
|
+
if (topTest.node !== node && topTest.true) {
|
|
6141
|
+
testStack.push((topTest = { node }));
|
|
6142
|
+
}
|
|
6140
6143
|
if (isStatement(node)) {
|
|
6141
6144
|
stmtStack.push(node);
|
|
6142
6145
|
}
|
|
@@ -6187,9 +6190,15 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6187
6190
|
let head;
|
|
6188
6191
|
if (node.type === "WhileStatement") {
|
|
6189
6192
|
head = localState.newBlock(top.continue);
|
|
6193
|
+
const body = {};
|
|
6194
|
+
testStack.push({
|
|
6195
|
+
node: node.test,
|
|
6196
|
+
true: body,
|
|
6197
|
+
false: top.break,
|
|
6198
|
+
});
|
|
6190
6199
|
state.traverse(node.test);
|
|
6191
|
-
localState.
|
|
6192
|
-
localState.newBlock();
|
|
6200
|
+
localState.unreachable = true;
|
|
6201
|
+
localState.newBlock(body);
|
|
6193
6202
|
}
|
|
6194
6203
|
else {
|
|
6195
6204
|
head = localState.newBlock();
|
|
@@ -6197,11 +6206,18 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6197
6206
|
state.traverse(node.body);
|
|
6198
6207
|
if (node.type === "DoWhileStatement") {
|
|
6199
6208
|
localState.newBlock(top.continue);
|
|
6209
|
+
testStack.push({
|
|
6210
|
+
node: node.test,
|
|
6211
|
+
true: head,
|
|
6212
|
+
false: top.break,
|
|
6213
|
+
});
|
|
6200
6214
|
state.traverse(node.test);
|
|
6201
|
-
localState.
|
|
6215
|
+
localState.unreachable = true;
|
|
6216
|
+
}
|
|
6217
|
+
else if (!localState.unreachable) {
|
|
6218
|
+
localState.addEdge(localState.curBlock, head);
|
|
6202
6219
|
}
|
|
6203
|
-
localState.
|
|
6204
|
-
localState.curBlock = top.break;
|
|
6220
|
+
localState.newBlock(top.break);
|
|
6205
6221
|
return [];
|
|
6206
6222
|
}
|
|
6207
6223
|
case "TryStatement": {
|
|
@@ -6298,9 +6314,15 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6298
6314
|
top.break = {};
|
|
6299
6315
|
top.continue = {};
|
|
6300
6316
|
if (node.test) {
|
|
6317
|
+
const body = {};
|
|
6318
|
+
testStack.push({
|
|
6319
|
+
node: node.test,
|
|
6320
|
+
true: body,
|
|
6321
|
+
false: top.break,
|
|
6322
|
+
});
|
|
6301
6323
|
state.traverse(node.test);
|
|
6302
|
-
localState.
|
|
6303
|
-
localState.newBlock();
|
|
6324
|
+
localState.unreachable = true;
|
|
6325
|
+
localState.newBlock(body);
|
|
6304
6326
|
}
|
|
6305
6327
|
state.traverse(node.body);
|
|
6306
6328
|
localState.newBlock(top.continue);
|
|
@@ -6322,39 +6344,91 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6322
6344
|
}
|
|
6323
6345
|
case "IfStatement":
|
|
6324
6346
|
case "ConditionalExpression": {
|
|
6325
|
-
|
|
6347
|
+
const consequent = {};
|
|
6326
6348
|
const alternate = {};
|
|
6327
|
-
|
|
6328
|
-
|
|
6349
|
+
const next = node.alternate ? {} : alternate;
|
|
6350
|
+
testStack.push({
|
|
6351
|
+
node: node.test,
|
|
6352
|
+
true: consequent,
|
|
6353
|
+
false: alternate,
|
|
6354
|
+
});
|
|
6355
|
+
state.traverse(node.test);
|
|
6356
|
+
localState.unreachable = true;
|
|
6357
|
+
if (topTest.true) {
|
|
6358
|
+
testStack.push({ ...topTest, node: node.consequent });
|
|
6359
|
+
}
|
|
6360
|
+
else {
|
|
6361
|
+
testStack.push({ node: node.consequent, true: next, false: next });
|
|
6362
|
+
}
|
|
6363
|
+
localState.newBlock(consequent);
|
|
6329
6364
|
state.traverse(node.consequent);
|
|
6330
|
-
const consequent = localState.unreachable
|
|
6331
|
-
? null
|
|
6332
|
-
: localState.curBlock;
|
|
6333
6365
|
localState.unreachable = true;
|
|
6334
|
-
localState.newBlock(alternate);
|
|
6335
6366
|
if (node.alternate) {
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
localState.newBlock();
|
|
6367
|
+
if (topTest.true) {
|
|
6368
|
+
testStack.push({ ...topTest, node: node.alternate });
|
|
6339
6369
|
}
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
if (localState.unreachable) {
|
|
6343
|
-
localState.newBlock();
|
|
6370
|
+
else {
|
|
6371
|
+
testStack.push({ node: node.alternate, true: next, false: next });
|
|
6344
6372
|
}
|
|
6345
|
-
localState.
|
|
6373
|
+
localState.newBlock(alternate);
|
|
6374
|
+
state.traverse(node.alternate);
|
|
6375
|
+
localState.unreachable = true;
|
|
6376
|
+
}
|
|
6377
|
+
if (next.preds) {
|
|
6378
|
+
/*
|
|
6379
|
+
* Given:
|
|
6380
|
+
* if (cond) {
|
|
6381
|
+
* } else if (cond2) {
|
|
6382
|
+
* } // no else
|
|
6383
|
+
*
|
|
6384
|
+
* cond2 will cause a branch to the second if's next block
|
|
6385
|
+
* But if topTest.true, we also need to ensure that next branches
|
|
6386
|
+
* to both true and false.
|
|
6387
|
+
*
|
|
6388
|
+
* So in *this* case, we have to skip the testStack.pop (or manually
|
|
6389
|
+
* add the edges here).
|
|
6390
|
+
*/
|
|
6391
|
+
localState.newBlock(next);
|
|
6392
|
+
}
|
|
6393
|
+
else if (topTest.node === node) {
|
|
6394
|
+
testStack.pop();
|
|
6346
6395
|
}
|
|
6347
6396
|
return [];
|
|
6348
6397
|
}
|
|
6349
6398
|
case "LogicalExpression": {
|
|
6399
|
+
const isAnd = node.operator === "&&" || node.operator === "and";
|
|
6400
|
+
const right = {};
|
|
6401
|
+
const next = {};
|
|
6402
|
+
if (isAnd) {
|
|
6403
|
+
testStack.push({
|
|
6404
|
+
node: node.left,
|
|
6405
|
+
true: right,
|
|
6406
|
+
false: topTest.false || next,
|
|
6407
|
+
});
|
|
6408
|
+
}
|
|
6409
|
+
else {
|
|
6410
|
+
testStack.push({
|
|
6411
|
+
node: node.left,
|
|
6412
|
+
true: topTest.true || next,
|
|
6413
|
+
false: right,
|
|
6414
|
+
});
|
|
6415
|
+
}
|
|
6350
6416
|
state.traverse(node.left);
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6417
|
+
localState.unreachable = true;
|
|
6418
|
+
localState.newBlock(right);
|
|
6419
|
+
testStack.push({
|
|
6420
|
+
node: node.right,
|
|
6421
|
+
true: topTest.true || next,
|
|
6422
|
+
false: topTest.false || next,
|
|
6423
|
+
});
|
|
6355
6424
|
state.traverse(node.right);
|
|
6356
|
-
localState.
|
|
6357
|
-
|
|
6425
|
+
localState.unreachable = true;
|
|
6426
|
+
if (next.preds) {
|
|
6427
|
+
localState.newBlock(next);
|
|
6428
|
+
}
|
|
6429
|
+
if (topTest.node === node) {
|
|
6430
|
+
testStack.pop();
|
|
6431
|
+
}
|
|
6358
6432
|
return [];
|
|
6359
6433
|
}
|
|
6360
6434
|
case "VariableDeclarator":
|
|
@@ -6415,6 +6489,7 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6415
6489
|
};
|
|
6416
6490
|
state.post = (node) => {
|
|
6417
6491
|
const curStmt = stmtStack[stmtStack.length - 1];
|
|
6492
|
+
const topTest = testStack[testStack.length - 1];
|
|
6418
6493
|
if (!state.inType) {
|
|
6419
6494
|
const throws = tryActive > 0 && mayThrow(node);
|
|
6420
6495
|
const event = notice(node, curStmt, throws);
|
|
@@ -6452,11 +6527,34 @@ function buildReducedGraph(state, func, notice) {
|
|
|
6452
6527
|
addEvent(localState.curBlock, event);
|
|
6453
6528
|
}
|
|
6454
6529
|
}
|
|
6530
|
+
if (localState.top().node === node) {
|
|
6531
|
+
localState.pop();
|
|
6532
|
+
}
|
|
6533
|
+
if (topTest.node === node) {
|
|
6534
|
+
testStack.pop();
|
|
6535
|
+
if (topTest.true && !localState.unreachable) {
|
|
6536
|
+
localState.addEdge(localState.curBlock, topTest.true);
|
|
6537
|
+
if (topTest.false !== topTest.true) {
|
|
6538
|
+
if (localState.curBlock.succs?.length !== 1) {
|
|
6539
|
+
throw new Error("Internal error: Unexpected successor edges");
|
|
6540
|
+
}
|
|
6541
|
+
localState.addEdge(localState.curBlock, topTest.false);
|
|
6542
|
+
const event = notice(node, curStmt, 1);
|
|
6543
|
+
if (event) {
|
|
6544
|
+
event.mayThrow = false;
|
|
6545
|
+
addEvent(localState.curBlock, event);
|
|
6546
|
+
localState.unreachable = true;
|
|
6547
|
+
}
|
|
6548
|
+
}
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6455
6551
|
if (curStmt === node) {
|
|
6456
6552
|
stmtStack.pop();
|
|
6457
6553
|
}
|
|
6458
|
-
if (localState.
|
|
6459
|
-
|
|
6554
|
+
else if (localState.unreachable &&
|
|
6555
|
+
curStmt.type === "BlockStatement" &&
|
|
6556
|
+
isStatement(node)) {
|
|
6557
|
+
return false;
|
|
6460
6558
|
}
|
|
6461
6559
|
return null;
|
|
6462
6560
|
};
|
|
@@ -6580,6 +6678,8 @@ function declFullName(decl) {
|
|
|
6580
6678
|
if ((0,external_api_cjs_namespaceObject.isStateNode)(decl))
|
|
6581
6679
|
return decl.fullName;
|
|
6582
6680
|
switch (decl.type) {
|
|
6681
|
+
case "Identifier":
|
|
6682
|
+
return decl.name;
|
|
6583
6683
|
case "BinaryExpression":
|
|
6584
6684
|
return decl.left.name;
|
|
6585
6685
|
case "EnumStringMember":
|
|
@@ -6587,7 +6687,7 @@ function declFullName(decl) {
|
|
|
6587
6687
|
? `${decl.id.name}:${(0,external_api_cjs_namespaceObject.formatAst)(decl.init)}`
|
|
6588
6688
|
: decl.id.name;
|
|
6589
6689
|
default:
|
|
6590
|
-
|
|
6690
|
+
unhandledType(decl);
|
|
6591
6691
|
}
|
|
6592
6692
|
}
|
|
6593
6693
|
function declName(decl) {
|
|
@@ -6608,7 +6708,7 @@ function declName(decl) {
|
|
|
6608
6708
|
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
6609
6709
|
}
|
|
6610
6710
|
}
|
|
6611
|
-
function
|
|
6711
|
+
function unhandledType(node) {
|
|
6612
6712
|
throw new Error(`Unhandled expression type: ${node.type}`);
|
|
6613
6713
|
}
|
|
6614
6714
|
function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wantsAllRefs) {
|
|
@@ -6677,6 +6777,9 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
|
|
|
6677
6777
|
return {
|
|
6678
6778
|
identifiers,
|
|
6679
6779
|
graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
|
|
6780
|
+
if (mayThrow === 1) {
|
|
6781
|
+
return null;
|
|
6782
|
+
}
|
|
6680
6783
|
const defs = liveStmts.get(node);
|
|
6681
6784
|
if (defs) {
|
|
6682
6785
|
liveStmts.delete(node);
|
|
@@ -6820,7 +6923,7 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
|
|
|
6820
6923
|
default:
|
|
6821
6924
|
if (!isExpression(node))
|
|
6822
6925
|
break;
|
|
6823
|
-
|
|
6926
|
+
unhandledType(node);
|
|
6824
6927
|
}
|
|
6825
6928
|
if (mayThrow) {
|
|
6826
6929
|
return { type: "exn", node, mayThrow };
|
|
@@ -6921,7 +7024,10 @@ function sizeBasedPRE(state, func) {
|
|
|
6921
7024
|
}, func.node.body);
|
|
6922
7025
|
variableDecl.end = variableDecl.start;
|
|
6923
7026
|
variableDecl.loc.end = variableDecl.loc.start;
|
|
6924
|
-
candidates.
|
|
7027
|
+
Array.from(candidates.keys())
|
|
7028
|
+
.map((decl) => [declFullName(decl), decl])
|
|
7029
|
+
.sort()
|
|
7030
|
+
.forEach(([, decl]) => {
|
|
6925
7031
|
let name;
|
|
6926
7032
|
let i = 0;
|
|
6927
7033
|
do {
|
|
@@ -6933,6 +7039,7 @@ function sizeBasedPRE(state, func) {
|
|
|
6933
7039
|
i++;
|
|
6934
7040
|
} while (true);
|
|
6935
7041
|
declMap.set(decl, name);
|
|
7042
|
+
const s = candidates.get(decl);
|
|
6936
7043
|
variableDecl.declarations.push(withLoc({
|
|
6937
7044
|
type: "VariableDeclarator",
|
|
6938
7045
|
id: withLoc({ type: "Identifier", name }, variableDecl),
|
|
@@ -6953,7 +7060,71 @@ function sizeBasedPRE(state, func) {
|
|
|
6953
7060
|
}
|
|
6954
7061
|
}
|
|
6955
7062
|
function buildPREGraph(state, func) {
|
|
6956
|
-
|
|
7063
|
+
const result = buildDataFlowGraph(state, func, (literal) => refCost(literal) > LocalRefCost, true, false);
|
|
7064
|
+
// Split blocks that contains refs and defs to the same thing,
|
|
7065
|
+
// to make it easier to pick and choose which refs are converted
|
|
7066
|
+
postOrderTraverse(result.graph, (block) => {
|
|
7067
|
+
if (!block.events) {
|
|
7068
|
+
return;
|
|
7069
|
+
}
|
|
7070
|
+
let modSeen = false;
|
|
7071
|
+
const refs = new Set();
|
|
7072
|
+
const defs = new Set();
|
|
7073
|
+
const splitBlock = (eventIndex) => {
|
|
7074
|
+
const newBlock = { ...block };
|
|
7075
|
+
delete newBlock.node;
|
|
7076
|
+
newBlock.events = block.events?.splice(eventIndex);
|
|
7077
|
+
newBlock.succs?.forEach((succ) => {
|
|
7078
|
+
const i = succ.preds.findIndex((b) => b === block);
|
|
7079
|
+
succ.preds[i] = newBlock;
|
|
7080
|
+
if (succ.expreds?.includes(block)) {
|
|
7081
|
+
succ.expreds.push(newBlock);
|
|
7082
|
+
}
|
|
7083
|
+
});
|
|
7084
|
+
block.succs = [newBlock];
|
|
7085
|
+
newBlock.preds = [block];
|
|
7086
|
+
delete newBlock.expreds;
|
|
7087
|
+
modSeen = false;
|
|
7088
|
+
refs.clear();
|
|
7089
|
+
defs.clear();
|
|
7090
|
+
};
|
|
7091
|
+
for (let i = block.events.length; i--;) {
|
|
7092
|
+
const event = block.events[i];
|
|
7093
|
+
switch (event.type) {
|
|
7094
|
+
case "ref":
|
|
7095
|
+
if ((0,external_util_cjs_namespaceObject.some)(event.decl, (decl) => decl.type === "Literal" ||
|
|
7096
|
+
(decl.type === "VariableDeclarator" &&
|
|
7097
|
+
decl.node.kind === "const"))) {
|
|
7098
|
+
break;
|
|
7099
|
+
}
|
|
7100
|
+
if (modSeen || defs.has(event.decl)) {
|
|
7101
|
+
splitBlock(i + 1);
|
|
7102
|
+
}
|
|
7103
|
+
refs.add(event.decl);
|
|
7104
|
+
break;
|
|
7105
|
+
case "def":
|
|
7106
|
+
if (refs.has(event.decl)) {
|
|
7107
|
+
splitBlock(i + 1);
|
|
7108
|
+
}
|
|
7109
|
+
defs.add(event.decl);
|
|
7110
|
+
break;
|
|
7111
|
+
case "mod":
|
|
7112
|
+
if (event.callees &&
|
|
7113
|
+
(0,external_util_cjs_namespaceObject.every)(event.callees, (callee) => callee.info === false)) {
|
|
7114
|
+
// calls known to have no side effects can be
|
|
7115
|
+
// dropped.
|
|
7116
|
+
block.events.splice(i, 1);
|
|
7117
|
+
break;
|
|
7118
|
+
}
|
|
7119
|
+
if (refs.size) {
|
|
7120
|
+
splitBlock(i + 1);
|
|
7121
|
+
}
|
|
7122
|
+
modSeen = true;
|
|
7123
|
+
break;
|
|
7124
|
+
}
|
|
7125
|
+
}
|
|
7126
|
+
});
|
|
7127
|
+
return result;
|
|
6957
7128
|
}
|
|
6958
7129
|
function anticipatedDecls() {
|
|
6959
7130
|
return new Map();
|
|
@@ -7527,64 +7698,2874 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
7527
7698
|
});
|
|
7528
7699
|
}
|
|
7529
7700
|
|
|
7530
|
-
;// CONCATENATED MODULE: ./src/
|
|
7701
|
+
;// CONCATENATED MODULE: ./src/type-flow/union-type.ts
|
|
7531
7702
|
|
|
7532
7703
|
|
|
7533
7704
|
|
|
7534
|
-
function
|
|
7535
|
-
|
|
7536
|
-
|
|
7705
|
+
function unionInto(to, from) {
|
|
7706
|
+
if (from.type === 0 || to === from)
|
|
7707
|
+
return false;
|
|
7708
|
+
if (to.type === 0) {
|
|
7709
|
+
to.type = from.type;
|
|
7710
|
+
to.value = from.value;
|
|
7711
|
+
return true;
|
|
7712
|
+
}
|
|
7713
|
+
const newTags = to.type | from.type;
|
|
7714
|
+
if (!(from.type & ~SingleTonTypeTagsConst)) {
|
|
7715
|
+
// - Adding singletons never affects the data.
|
|
7716
|
+
if (newTags === to.type)
|
|
7717
|
+
return false;
|
|
7718
|
+
to.type = newTags;
|
|
7719
|
+
return true;
|
|
7720
|
+
}
|
|
7721
|
+
if (newTags === to.type) {
|
|
7722
|
+
if (to.value === from.value || to.value == null) {
|
|
7723
|
+
return false;
|
|
7724
|
+
}
|
|
7725
|
+
if (from.value == null) {
|
|
7726
|
+
clearValuesUnder(to, from.type);
|
|
7727
|
+
return true;
|
|
7728
|
+
}
|
|
7729
|
+
return mergeMultiple(to, from);
|
|
7730
|
+
}
|
|
7731
|
+
if (to.value != null) {
|
|
7732
|
+
if (from.value == null) {
|
|
7733
|
+
clearValuesUnder(to, from.type);
|
|
7734
|
+
return true;
|
|
7735
|
+
}
|
|
7736
|
+
return mergeMultiple(to, from);
|
|
7737
|
+
}
|
|
7738
|
+
if (from.value == null) {
|
|
7739
|
+
to.type = newTags;
|
|
7740
|
+
return true;
|
|
7741
|
+
}
|
|
7742
|
+
const tmp = cloneType(from);
|
|
7743
|
+
clearValuesUnder(tmp, to.type);
|
|
7744
|
+
to.type = tmp.type;
|
|
7745
|
+
to.value = tmp.value;
|
|
7746
|
+
return true;
|
|
7747
|
+
}
|
|
7748
|
+
function mergeMultiple(to, from) {
|
|
7749
|
+
const newTags = to.type | from.type;
|
|
7750
|
+
let anyChanged = newTags !== to.type;
|
|
7751
|
+
let mask = 0;
|
|
7752
|
+
const result = {};
|
|
7753
|
+
forEachUnionComponent(to, newTags, (tag, tov) => {
|
|
7754
|
+
const fromv = getUnionComponent(from, tag);
|
|
7755
|
+
if (tov != null) {
|
|
7756
|
+
if (fromv != null) {
|
|
7757
|
+
const [value, changed] = mergeSingle(tag, tov, fromv);
|
|
7758
|
+
if (changed)
|
|
7759
|
+
anyChanged = true;
|
|
7760
|
+
if (value) {
|
|
7761
|
+
mask |= tag;
|
|
7762
|
+
result[tag] = value;
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
else if (!(from.type & tag)) {
|
|
7766
|
+
// from doesn't contribute to this tag,
|
|
7767
|
+
// so just keep it. No change.
|
|
7768
|
+
mask |= tag;
|
|
7769
|
+
result[tag] = tov;
|
|
7770
|
+
}
|
|
7771
|
+
else {
|
|
7772
|
+
// We dropped the data for this tag, so
|
|
7773
|
+
// things changed.
|
|
7774
|
+
anyChanged = true;
|
|
7775
|
+
}
|
|
7776
|
+
}
|
|
7777
|
+
else if (fromv && !(to.type & tag)) {
|
|
7778
|
+
// to doesn't contribute to this tag,
|
|
7779
|
+
// so just keep from's component.
|
|
7780
|
+
// this is new, so it changed.
|
|
7781
|
+
anyChanged = true;
|
|
7782
|
+
mask |= tag;
|
|
7783
|
+
result[tag] = fromv;
|
|
7784
|
+
}
|
|
7785
|
+
});
|
|
7786
|
+
if (!anyChanged)
|
|
7787
|
+
return false;
|
|
7788
|
+
to.type = newTags;
|
|
7789
|
+
if (!mask) {
|
|
7790
|
+
delete to.value;
|
|
7791
|
+
return true;
|
|
7792
|
+
}
|
|
7793
|
+
if (hasUnionData(newTags)) {
|
|
7794
|
+
to.value = result;
|
|
7795
|
+
to.value.mask = mask;
|
|
7796
|
+
return true;
|
|
7797
|
+
}
|
|
7798
|
+
if (mask & (mask - 1)) {
|
|
7799
|
+
throw new Error("Union incorrectly produced a UnionData");
|
|
7800
|
+
}
|
|
7801
|
+
to.value = result[mask];
|
|
7802
|
+
return true;
|
|
7803
|
+
}
|
|
7804
|
+
function tryUnion(to, from) {
|
|
7805
|
+
to = cloneType(to);
|
|
7806
|
+
if (unionInto(to, from))
|
|
7807
|
+
return to;
|
|
7808
|
+
return null;
|
|
7809
|
+
}
|
|
7810
|
+
function mergeSingle(type, to, from) {
|
|
7811
|
+
switch (type) {
|
|
7812
|
+
case TypeTag.Number:
|
|
7813
|
+
case TypeTag.Long:
|
|
7814
|
+
case TypeTag.Float:
|
|
7815
|
+
case TypeTag.Double:
|
|
7816
|
+
case TypeTag.Char:
|
|
7817
|
+
case TypeTag.String:
|
|
7818
|
+
case TypeTag.Symbol:
|
|
7819
|
+
if (to === from) {
|
|
7820
|
+
return [to, false];
|
|
7821
|
+
}
|
|
7822
|
+
return [null, true];
|
|
7823
|
+
case TypeTag.Array: {
|
|
7824
|
+
const merged = tryUnion(to, from);
|
|
7825
|
+
return [merged || to, merged != null];
|
|
7826
|
+
}
|
|
7827
|
+
case TypeTag.Dictionary: {
|
|
7828
|
+
const { key, value } = to;
|
|
7829
|
+
const keyChange = tryUnion(key, from.key);
|
|
7830
|
+
const valueChange = tryUnion(value, from.value);
|
|
7831
|
+
if (keyChange || valueChange) {
|
|
7832
|
+
return [{ key: keyChange || key, value: valueChange || value }, true];
|
|
7833
|
+
}
|
|
7834
|
+
return [to, false];
|
|
7835
|
+
}
|
|
7836
|
+
case TypeTag.Module:
|
|
7837
|
+
case TypeTag.Function:
|
|
7838
|
+
case TypeTag.Class:
|
|
7839
|
+
case TypeTag.Typedef:
|
|
7840
|
+
return mergeStateDecls(to, from);
|
|
7841
|
+
case TypeTag.Object: {
|
|
7842
|
+
const klass = to.klass;
|
|
7843
|
+
const [obj, objChanged] = mergeObjectValues(to.obj, from.obj);
|
|
7844
|
+
const klassChanged = tryUnion(klass, from.klass);
|
|
7845
|
+
if (klassChanged || objChanged) {
|
|
7846
|
+
if (obj) {
|
|
7847
|
+
return [{ klass: klassChanged, obj }, true];
|
|
7848
|
+
}
|
|
7849
|
+
return [{ klass: klassChanged }, true];
|
|
7850
|
+
}
|
|
7851
|
+
return [to, false];
|
|
7852
|
+
}
|
|
7853
|
+
case TypeTag.Enum: {
|
|
7854
|
+
const toE = to;
|
|
7855
|
+
const fromE = from;
|
|
7856
|
+
if (toE.enum !== fromE.enum) {
|
|
7857
|
+
return [null, true];
|
|
7858
|
+
}
|
|
7859
|
+
if (!toE.value) {
|
|
7860
|
+
return [toE, false];
|
|
7861
|
+
}
|
|
7862
|
+
if (!fromE.value) {
|
|
7863
|
+
delete toE.value;
|
|
7864
|
+
return [toE, true];
|
|
7865
|
+
}
|
|
7866
|
+
const toValue = tryUnion(toE.value, fromE.value);
|
|
7867
|
+
if (toValue) {
|
|
7868
|
+
return [{ enum: toE.enum, value: toValue }, true];
|
|
7869
|
+
}
|
|
7870
|
+
return [toE, false];
|
|
7871
|
+
}
|
|
7872
|
+
}
|
|
7873
|
+
throw new Error(`Unexpected type ${type}`);
|
|
7874
|
+
}
|
|
7875
|
+
function mergeObjectValues(to, from) {
|
|
7876
|
+
if (!to) {
|
|
7877
|
+
return [to, false];
|
|
7878
|
+
}
|
|
7879
|
+
if (!from) {
|
|
7880
|
+
return [from, true];
|
|
7881
|
+
}
|
|
7882
|
+
let empty = true;
|
|
7883
|
+
let result = to;
|
|
7884
|
+
Object.entries(to).forEach(([key, value]) => {
|
|
7885
|
+
if (!hasProperty(from, key)) {
|
|
7886
|
+
if (result === to)
|
|
7887
|
+
result = { ...result };
|
|
7888
|
+
delete result[key];
|
|
7889
|
+
return;
|
|
7890
|
+
}
|
|
7891
|
+
const rep = cloneType(value);
|
|
7892
|
+
if (unionInto(rep, from[key])) {
|
|
7893
|
+
if (result === to)
|
|
7894
|
+
result = { ...result };
|
|
7895
|
+
result[key] = rep;
|
|
7896
|
+
}
|
|
7897
|
+
empty = false;
|
|
7898
|
+
});
|
|
7899
|
+
if (empty) {
|
|
7900
|
+
return [undefined, true];
|
|
7901
|
+
}
|
|
7902
|
+
return [result, result !== to];
|
|
7903
|
+
}
|
|
7904
|
+
function mergeStateDecls(to, from) {
|
|
7905
|
+
let changed = false;
|
|
7906
|
+
let result = to;
|
|
7907
|
+
(0,external_util_cjs_namespaceObject.forEach)(from, (v) => {
|
|
7908
|
+
if ((0,external_util_cjs_namespaceObject.some)(to, (t) => t === v)) {
|
|
7909
|
+
return;
|
|
7910
|
+
}
|
|
7911
|
+
if (Array.isArray(result)) {
|
|
7912
|
+
if (result === to) {
|
|
7913
|
+
result = [...result, v];
|
|
7914
|
+
}
|
|
7915
|
+
else {
|
|
7916
|
+
result.push(v);
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7919
|
+
else {
|
|
7920
|
+
result = [to, v];
|
|
7921
|
+
}
|
|
7922
|
+
changed = true;
|
|
7923
|
+
});
|
|
7924
|
+
return [result, changed];
|
|
7925
|
+
}
|
|
7926
|
+
// precondition: hasUnionData
|
|
7927
|
+
function nonUnionDataMask(tag) {
|
|
7928
|
+
if (tag & (tag - 1)) {
|
|
7929
|
+
// More than one bit set, but it doesn't have
|
|
7930
|
+
// UnionData, so it must either have a single bit
|
|
7931
|
+
// under UnionDataTypeTagsConst, or a single bit
|
|
7932
|
+
// under ValueTypeTagsConst.
|
|
7933
|
+
return tag & UnionDataTypeTagsConst || tag & ValueTypeTagsConst;
|
|
7934
|
+
}
|
|
7935
|
+
return tag;
|
|
7936
|
+
}
|
|
7937
|
+
/*
|
|
7938
|
+
* Remove any data associated with tag.
|
|
7939
|
+
* Add (or remove) tag to (or from) v.type, according
|
|
7940
|
+
* to clearTag.
|
|
7941
|
+
*/
|
|
7942
|
+
function clearValuesUnder(v, tag, clearTag = false) {
|
|
7943
|
+
const newTag = clearTag ? v.type & ~tag : v.type | tag;
|
|
7944
|
+
// If the incoming type consists of singletons,
|
|
7945
|
+
// we can always merge it without affecting our data.
|
|
7946
|
+
tag &= ~SingleTonTypeTagsConst;
|
|
7947
|
+
if (!tag) {
|
|
7948
|
+
v.type = newTag;
|
|
7537
7949
|
return;
|
|
7538
7950
|
}
|
|
7539
|
-
|
|
7540
|
-
|
|
7951
|
+
// We only keep data for ValueTypeTags if there is
|
|
7952
|
+
// only one of them. If the type being merged has
|
|
7953
|
+
// no data for one of them, the resulting type has
|
|
7954
|
+
// no data for any of them.
|
|
7955
|
+
if (tag & ValueTypeTagsConst) {
|
|
7956
|
+
tag |= ValueTypeTagsConst;
|
|
7957
|
+
}
|
|
7958
|
+
if (!hasUnionData(v.type)) {
|
|
7959
|
+
// get the single bit corresponding to v's data
|
|
7960
|
+
const dataMask = nonUnionDataMask(v.type);
|
|
7961
|
+
if (dataMask & tag) {
|
|
7962
|
+
// the merging type has no data for our
|
|
7963
|
+
// exact type; so delete all our data.
|
|
7964
|
+
// eg Number<1> or String => Number or String
|
|
7965
|
+
v.type = newTag;
|
|
7966
|
+
delete v.value;
|
|
7967
|
+
return;
|
|
7968
|
+
}
|
|
7969
|
+
if (dataMask & ValueTypeTagsConst) {
|
|
7970
|
+
// assert(tag & UnionDataTypeTags)
|
|
7971
|
+
// v had data corresponding to one of the
|
|
7972
|
+
// ValueTypeTags. But tag adds at least
|
|
7973
|
+
// one new bit. So drop the data.
|
|
7974
|
+
// eg Number<1> or Array => Number or Array
|
|
7975
|
+
delete v.value;
|
|
7976
|
+
v.type = newTag;
|
|
7977
|
+
return;
|
|
7978
|
+
}
|
|
7979
|
+
if (hasUnionData(newTag)) {
|
|
7980
|
+
// v had data corresponding to one of the
|
|
7981
|
+
// UnionDataTypeTags, and tag adds at least
|
|
7982
|
+
// one new bit. Keep the data, but move it into
|
|
7983
|
+
// a UnionData.
|
|
7984
|
+
// eg Array<Number> or Dictionary remains as is.
|
|
7985
|
+
const mask = v.type & UnionDataTypeTagsConst;
|
|
7986
|
+
v.value = {
|
|
7987
|
+
[mask]: v.value,
|
|
7988
|
+
mask,
|
|
7989
|
+
};
|
|
7990
|
+
}
|
|
7991
|
+
v.type = newTag;
|
|
7992
|
+
return;
|
|
7541
7993
|
}
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7994
|
+
v.type = newTag;
|
|
7995
|
+
const unionData = v.value;
|
|
7996
|
+
let remain = unionData.mask & ~tag;
|
|
7997
|
+
if (!remain) {
|
|
7998
|
+
delete v.value;
|
|
7999
|
+
return;
|
|
8000
|
+
}
|
|
8001
|
+
const newData = { mask: remain };
|
|
8002
|
+
while (remain) {
|
|
8003
|
+
const next = remain & (remain - 1);
|
|
8004
|
+
const bit = (remain - next);
|
|
8005
|
+
newData[bit] = unionData[bit];
|
|
8006
|
+
remain = next;
|
|
8007
|
+
}
|
|
8008
|
+
if (!hasUnionData(newTag)) {
|
|
8009
|
+
v.value = newData[(newTag & UnionDataTypeTagsConst)];
|
|
8010
|
+
}
|
|
8011
|
+
else {
|
|
8012
|
+
v.value = newData;
|
|
8013
|
+
}
|
|
8014
|
+
}
|
|
8015
|
+
|
|
8016
|
+
;// CONCATENATED MODULE: ./src/type-flow/types.ts
|
|
8017
|
+
|
|
8018
|
+
|
|
8019
|
+
|
|
8020
|
+
|
|
8021
|
+
|
|
8022
|
+
|
|
8023
|
+
/**
|
|
8024
|
+
* TypeBit gives the position of the 1 bit in TypeTag
|
|
8025
|
+
*/
|
|
8026
|
+
var TypeBit;
|
|
8027
|
+
(function (TypeBit) {
|
|
8028
|
+
TypeBit[TypeBit["Null"] = 0] = "Null";
|
|
8029
|
+
TypeBit[TypeBit["False"] = 1] = "False";
|
|
8030
|
+
TypeBit[TypeBit["True"] = 2] = "True";
|
|
8031
|
+
TypeBit[TypeBit["Number"] = 3] = "Number";
|
|
8032
|
+
TypeBit[TypeBit["Long"] = 4] = "Long";
|
|
8033
|
+
TypeBit[TypeBit["Float"] = 5] = "Float";
|
|
8034
|
+
TypeBit[TypeBit["Double"] = 6] = "Double";
|
|
8035
|
+
TypeBit[TypeBit["Char"] = 7] = "Char";
|
|
8036
|
+
TypeBit[TypeBit["String"] = 8] = "String";
|
|
8037
|
+
TypeBit[TypeBit["Array"] = 9] = "Array";
|
|
8038
|
+
TypeBit[TypeBit["Dictionary"] = 10] = "Dictionary";
|
|
8039
|
+
TypeBit[TypeBit["Module"] = 11] = "Module";
|
|
8040
|
+
TypeBit[TypeBit["Function"] = 12] = "Function";
|
|
8041
|
+
TypeBit[TypeBit["Class"] = 13] = "Class";
|
|
8042
|
+
TypeBit[TypeBit["Object"] = 14] = "Object";
|
|
8043
|
+
TypeBit[TypeBit["Enum"] = 15] = "Enum";
|
|
8044
|
+
TypeBit[TypeBit["Symbol"] = 16] = "Symbol";
|
|
8045
|
+
TypeBit[TypeBit["Typedef"] = 17] = "Typedef";
|
|
8046
|
+
})(TypeBit || (TypeBit = {}));
|
|
8047
|
+
var TypeTag;
|
|
8048
|
+
(function (TypeTag) {
|
|
8049
|
+
TypeTag[TypeTag["Never"] = 0] = "Never";
|
|
8050
|
+
TypeTag[TypeTag["Null"] = 1] = "Null";
|
|
8051
|
+
TypeTag[TypeTag["False"] = 2] = "False";
|
|
8052
|
+
TypeTag[TypeTag["True"] = 4] = "True";
|
|
8053
|
+
TypeTag[TypeTag["Boolean"] = 6] = "Boolean";
|
|
8054
|
+
TypeTag[TypeTag["Number"] = 8] = "Number";
|
|
8055
|
+
TypeTag[TypeTag["Long"] = 16] = "Long";
|
|
8056
|
+
TypeTag[TypeTag["Float"] = 32] = "Float";
|
|
8057
|
+
TypeTag[TypeTag["Double"] = 64] = "Double";
|
|
8058
|
+
TypeTag[TypeTag["Numeric"] = 120] = "Numeric";
|
|
8059
|
+
TypeTag[TypeTag["Char"] = 128] = "Char";
|
|
8060
|
+
TypeTag[TypeTag["String"] = 256] = "String";
|
|
8061
|
+
TypeTag[TypeTag["Array"] = 512] = "Array";
|
|
8062
|
+
TypeTag[TypeTag["Dictionary"] = 1024] = "Dictionary";
|
|
8063
|
+
TypeTag[TypeTag["Module"] = 2048] = "Module";
|
|
8064
|
+
TypeTag[TypeTag["Function"] = 4096] = "Function";
|
|
8065
|
+
TypeTag[TypeTag["Class"] = 8192] = "Class";
|
|
8066
|
+
TypeTag[TypeTag["Object"] = 16384] = "Object";
|
|
8067
|
+
TypeTag[TypeTag["Enum"] = 32768] = "Enum";
|
|
8068
|
+
TypeTag[TypeTag["Symbol"] = 65536] = "Symbol";
|
|
8069
|
+
TypeTag[TypeTag["Typedef"] = 131072] = "Typedef";
|
|
8070
|
+
TypeTag[TypeTag["Any"] = 262143] = "Any";
|
|
8071
|
+
})(TypeTag || (TypeTag = {}));
|
|
8072
|
+
const SingleTonTypeTagsConst = TypeTag.Null | TypeTag.False | TypeTag.True;
|
|
8073
|
+
const UnionDataTypeTagsConst = TypeTag.Array |
|
|
8074
|
+
TypeTag.Dictionary |
|
|
8075
|
+
TypeTag.Module |
|
|
8076
|
+
TypeTag.Function |
|
|
8077
|
+
TypeTag.Class |
|
|
8078
|
+
TypeTag.Object |
|
|
8079
|
+
TypeTag.Enum |
|
|
8080
|
+
TypeTag.Typedef;
|
|
8081
|
+
const ValueTypeTagsConst = TypeTag.Number |
|
|
8082
|
+
TypeTag.Long |
|
|
8083
|
+
TypeTag.Float |
|
|
8084
|
+
TypeTag.Double |
|
|
8085
|
+
TypeTag.Char |
|
|
8086
|
+
TypeTag.String |
|
|
8087
|
+
TypeTag.Symbol;
|
|
8088
|
+
const ObjectLikeTagsConst = TypeTag.Boolean |
|
|
8089
|
+
ValueTypeTagsConst |
|
|
8090
|
+
TypeTag.Array |
|
|
8091
|
+
TypeTag.Dictionary |
|
|
8092
|
+
TypeTag.Enum;
|
|
8093
|
+
function isExact(v) {
|
|
8094
|
+
// check that there is exactly one bit set
|
|
8095
|
+
return v.type !== 0 && !(v.type & (v.type - 1));
|
|
8096
|
+
}
|
|
8097
|
+
function isUnion(v) {
|
|
8098
|
+
return v.type !== 0 && !isExact(v);
|
|
8099
|
+
}
|
|
8100
|
+
function isSingleton(v) {
|
|
8101
|
+
return isExact(v) && (v.type & SingleTonTypeTagsConst) !== 0;
|
|
8102
|
+
}
|
|
8103
|
+
function hasValue(v) {
|
|
8104
|
+
return (isExact(v) &&
|
|
8105
|
+
((v.type & SingleTonTypeTagsConst) !== 0 || v.value !== undefined));
|
|
8106
|
+
}
|
|
8107
|
+
function lookupByFullName(state, fullName) {
|
|
8108
|
+
return fullName.split(".").reduce((results, part) => {
|
|
8109
|
+
return results
|
|
8110
|
+
.flatMap((result) => (0,external_api_cjs_namespaceObject.isStateNode)(result)
|
|
8111
|
+
? result.decls?.[part] || result.type_decls?.[part]
|
|
8112
|
+
: null)
|
|
8113
|
+
.filter((sn) => !!sn);
|
|
8114
|
+
}, [state.stack[0]]);
|
|
8115
|
+
}
|
|
8116
|
+
function getMethod(state) {
|
|
8117
|
+
const results = lookupByFullName(state, "Toybox.Lang.Method");
|
|
8118
|
+
if (results.length !== 1) {
|
|
8119
|
+
throw new Error("Internal error: Didn't find Toybox.Lang.Method");
|
|
8120
|
+
}
|
|
8121
|
+
if (results[0].type !== "ClassDeclaration") {
|
|
8122
|
+
throw new Error("Internal error: Toybox.Lang.Method was not a Class");
|
|
8123
|
+
}
|
|
8124
|
+
return results[0];
|
|
8125
|
+
}
|
|
8126
|
+
function cloneType(t) {
|
|
8127
|
+
return { ...t };
|
|
8128
|
+
}
|
|
8129
|
+
function typeFromTypeStateNode(state, sn, classVsObj) {
|
|
8130
|
+
switch (sn.type) {
|
|
8131
|
+
case "ClassDeclaration": {
|
|
8132
|
+
switch (sn.fullName) {
|
|
8133
|
+
case "$.Toybox.Lang.Boolean":
|
|
8134
|
+
return { type: TypeTag.Boolean };
|
|
8135
|
+
case "$.Toybox.Lang.Number":
|
|
8136
|
+
return { type: TypeTag.Number };
|
|
8137
|
+
case "$.Toybox.Lang.Long":
|
|
8138
|
+
return { type: TypeTag.Long };
|
|
8139
|
+
case "$.Toybox.Lang.Float":
|
|
8140
|
+
return { type: TypeTag.Float };
|
|
8141
|
+
case "$.Toybox.Lang.Double":
|
|
8142
|
+
return { type: TypeTag.Double };
|
|
8143
|
+
case "$.Toybox.Lang.Char":
|
|
8144
|
+
return { type: TypeTag.Char };
|
|
8145
|
+
case "$.Toybox.Lang.String":
|
|
8146
|
+
return { type: TypeTag.String };
|
|
8147
|
+
case "$.Toybox.Lang.Array":
|
|
8148
|
+
return { type: TypeTag.Array };
|
|
8149
|
+
case "$.Toybox.Lang.Dictionary":
|
|
8150
|
+
return { type: TypeTag.Dictionary };
|
|
8151
|
+
case "$.Toybox.Lang.Symbol":
|
|
8152
|
+
return { type: TypeTag.Symbol };
|
|
8153
|
+
case "$.Toybox.Lang.Object":
|
|
8154
|
+
return { type: TypeTag.Object };
|
|
8155
|
+
}
|
|
8156
|
+
const cls = { type: TypeTag.Class, value: sn };
|
|
8157
|
+
if (classVsObj)
|
|
8158
|
+
return cls;
|
|
8159
|
+
return { type: TypeTag.Object, value: { klass: cls } };
|
|
8160
|
+
}
|
|
8161
|
+
case "FunctionDeclaration":
|
|
8162
|
+
return { type: TypeTag.Function, value: sn };
|
|
8163
|
+
case "ModuleDeclaration":
|
|
8164
|
+
return { type: TypeTag.Module, value: sn };
|
|
8165
|
+
case "Program":
|
|
8166
|
+
return { type: TypeTag.Module };
|
|
8167
|
+
case "EnumDeclaration":
|
|
8168
|
+
return { type: TypeTag.Enum, value: { enum: sn } };
|
|
8169
|
+
case "EnumStringMember": {
|
|
8170
|
+
const e = state.enumMap?.get(sn);
|
|
8171
|
+
const value = sn.init?.type === "Literal"
|
|
8172
|
+
? typeFromLiteral(sn.init)
|
|
8173
|
+
: { type: TypeTag.Numeric | TypeTag.String };
|
|
8174
|
+
if (e) {
|
|
8175
|
+
return { type: TypeTag.Enum, value: { enum: e, value } };
|
|
8176
|
+
}
|
|
8177
|
+
return value;
|
|
8178
|
+
}
|
|
8179
|
+
case "TypedefDeclaration": {
|
|
8180
|
+
if (sn.resolvedType) {
|
|
8181
|
+
return sn.resolvedType;
|
|
8182
|
+
}
|
|
8183
|
+
if (sn.isExpanding) {
|
|
8184
|
+
sn.isRecursive = true;
|
|
8185
|
+
return { type: TypeTag.Typedef, value: sn };
|
|
8186
|
+
}
|
|
8187
|
+
sn.isExpanding = true;
|
|
8188
|
+
const result = typeFromTypespec(state, sn.node.ts.argument, sn.stack);
|
|
8189
|
+
delete sn.isExpanding;
|
|
8190
|
+
if (sn.isRecursive) {
|
|
8191
|
+
// Something like
|
|
8192
|
+
// typedef Foo as Number or String or Foo;
|
|
8193
|
+
// is pointless. Its just the same as
|
|
8194
|
+
// typedef Foo as Number or String;
|
|
8195
|
+
// Recursive typedefs are only useful when the
|
|
8196
|
+
// recursion happens under a generic.
|
|
8197
|
+
// So check for that, and remove it.
|
|
8198
|
+
if (result.type & TypeTag.Typedef) {
|
|
8199
|
+
const value = getUnionComponent(result, TypeTag.Typedef);
|
|
8200
|
+
if (value) {
|
|
8201
|
+
const a = [];
|
|
8202
|
+
(0,external_util_cjs_namespaceObject.forEach)(value, (v) => {
|
|
8203
|
+
if (v !== sn)
|
|
8204
|
+
a.push(v);
|
|
8205
|
+
});
|
|
8206
|
+
clearValuesUnder(result, TypeTag.Typedef, true);
|
|
8207
|
+
if (a.length) {
|
|
8208
|
+
unionInto(result, {
|
|
8209
|
+
type: TypeTag.Typedef,
|
|
8210
|
+
value: a.length > 1 ? a : a[0],
|
|
8211
|
+
});
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
7551
8215
|
}
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
stack.push(node.consequent);
|
|
7560
|
-
break;
|
|
7561
|
-
case "BlockStatement":
|
|
7562
|
-
stack.push(node.body);
|
|
7563
|
-
break;
|
|
8216
|
+
sn.resolvedType = result;
|
|
8217
|
+
return result;
|
|
8218
|
+
}
|
|
8219
|
+
case "VariableDeclarator":
|
|
8220
|
+
if (sn.node.kind === "const" && sn.node.init) {
|
|
8221
|
+
if (sn.node.init.type === "Literal") {
|
|
8222
|
+
return typeFromLiteral(sn.node.init);
|
|
7564
8223
|
}
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
8224
|
+
const [value] = getNodeValue(sn.node.init);
|
|
8225
|
+
if (value) {
|
|
8226
|
+
return evaluateExpr(state, sn.node.init).value;
|
|
8227
|
+
}
|
|
8228
|
+
}
|
|
8229
|
+
if (sn.node.id.type === "BinaryExpression") {
|
|
8230
|
+
return typeFromTypespec(state, sn.node.id.right, sn.stack);
|
|
8231
|
+
}
|
|
8232
|
+
return { type: TypeTag.Any };
|
|
8233
|
+
}
|
|
8234
|
+
throw new Error(`Internal error: Unexpected StateNodeDecl.type: ${sn.type}`);
|
|
8235
|
+
}
|
|
8236
|
+
function typeFromTypeStateNodes(state, sns, classVsObj) {
|
|
8237
|
+
return sns.reduce((cur, sn) => {
|
|
8238
|
+
unionInto(cur, typeFromTypeStateNode(state, sn, classVsObj));
|
|
8239
|
+
return cur;
|
|
8240
|
+
}, { type: TypeTag.Never });
|
|
8241
|
+
}
|
|
8242
|
+
function typeFromSingleTypeSpec(state, type, stack) {
|
|
8243
|
+
if (typeof type === "string") {
|
|
8244
|
+
type = { type: "TypeSpecPart", name: type };
|
|
8245
|
+
}
|
|
8246
|
+
switch (type.type) {
|
|
8247
|
+
case "ObjectExpression":
|
|
8248
|
+
return { type: TypeTag.Dictionary };
|
|
8249
|
+
case "TypeSpecPart": {
|
|
8250
|
+
if (type.body) {
|
|
8251
|
+
// this is an interface declaration.
|
|
8252
|
+
// For now, make it an instance of an unknown class.
|
|
8253
|
+
return { type: TypeTag.Object };
|
|
8254
|
+
}
|
|
8255
|
+
if (type.callspec) {
|
|
8256
|
+
// only legal thing here is Method(<args>) as <result>
|
|
8257
|
+
// For now, make it an instance of an unknown class.
|
|
8258
|
+
return {
|
|
8259
|
+
type: TypeTag.Object,
|
|
8260
|
+
value: { klass: { type: TypeTag.Class, value: getMethod(state) } },
|
|
8261
|
+
};
|
|
8262
|
+
}
|
|
8263
|
+
const id = typeof type.name === "string" ? makeScopedName(type.name) : type.name;
|
|
8264
|
+
const [, results] = state.lookupType(id, null, stack);
|
|
8265
|
+
if (!results) {
|
|
8266
|
+
if (id.type === "Identifier") {
|
|
8267
|
+
switch (id.name) {
|
|
8268
|
+
case "Void":
|
|
8269
|
+
case "Null":
|
|
8270
|
+
return { type: TypeTag.Null };
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
8273
|
+
const level = state.config?.checkInvalidSymbols;
|
|
8274
|
+
if (level !== "OFF") {
|
|
8275
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, id.loc, `Unable to resolve type ${(0,external_api_cjs_namespaceObject.formatAst)(id)}`, level || "WARNING");
|
|
8276
|
+
}
|
|
8277
|
+
return { type: TypeTag.Any };
|
|
8278
|
+
}
|
|
8279
|
+
const resultType = results.reduce((cur, lookupDefn) => {
|
|
8280
|
+
unionInto(cur, typeFromTypeStateNodes(state, lookupDefn.results));
|
|
8281
|
+
return cur;
|
|
8282
|
+
}, { type: TypeTag.Never });
|
|
8283
|
+
if (type.generics) {
|
|
8284
|
+
if (resultType.type === TypeTag.Array && type.generics.length === 1) {
|
|
8285
|
+
resultType.value = typeFromTypespec(state, type.generics[0], stack);
|
|
8286
|
+
}
|
|
8287
|
+
else if (resultType.type === TypeTag.Dictionary &&
|
|
8288
|
+
type.generics.length === 2) {
|
|
8289
|
+
resultType.value = {
|
|
8290
|
+
key: typeFromTypespec(state, type.generics[0], stack),
|
|
8291
|
+
value: typeFromTypespec(state, type.generics[1], stack),
|
|
8292
|
+
};
|
|
8293
|
+
}
|
|
8294
|
+
}
|
|
8295
|
+
return resultType;
|
|
8296
|
+
}
|
|
8297
|
+
}
|
|
8298
|
+
}
|
|
8299
|
+
function typeFromTypespec(state, ts, stack) {
|
|
8300
|
+
if (!state.config?.trustDeclaredTypes) {
|
|
8301
|
+
if (ts.ts.length === 1 && typeof ts.ts[0] === "string") {
|
|
8302
|
+
const e = lookupByFullName(state, ts.ts[0]);
|
|
8303
|
+
if (e && e.length === 1 && e[0].type === "EnumDeclaration") {
|
|
8304
|
+
return {
|
|
8305
|
+
type: TypeTag.Enum,
|
|
8306
|
+
value: { enum: e[0] },
|
|
8307
|
+
};
|
|
8308
|
+
}
|
|
8309
|
+
}
|
|
8310
|
+
return { type: TypeTag.Any };
|
|
8311
|
+
}
|
|
8312
|
+
return ts.ts.reduce((cur, type) => {
|
|
8313
|
+
unionInto(cur, typeFromSingleTypeSpec(state, type, stack));
|
|
8314
|
+
return cur;
|
|
8315
|
+
}, { type: TypeTag.Never });
|
|
8316
|
+
}
|
|
8317
|
+
function typeFromLiteral(literal) {
|
|
8318
|
+
const [value, type] = getNodeValue(literal);
|
|
8319
|
+
switch (type) {
|
|
8320
|
+
case "Null":
|
|
8321
|
+
return { type: TypeTag.Null };
|
|
8322
|
+
case "Boolean":
|
|
8323
|
+
return literal.value ? { type: TypeTag.True } : { type: TypeTag.False };
|
|
8324
|
+
case "Number":
|
|
8325
|
+
return { type: TypeTag.Number, value: value.value };
|
|
8326
|
+
case "Long":
|
|
8327
|
+
return { type: TypeTag.Long, value: BigInt(value.value) };
|
|
8328
|
+
case "Float":
|
|
8329
|
+
return { type: TypeTag.Float, value: value.value };
|
|
8330
|
+
case "Double":
|
|
8331
|
+
return { type: TypeTag.Double, value: value.value };
|
|
8332
|
+
case "Char":
|
|
8333
|
+
return { type: TypeTag.Char, value: value.value };
|
|
8334
|
+
case "String":
|
|
8335
|
+
return { type: TypeTag.String, value: value.value };
|
|
8336
|
+
default:
|
|
8337
|
+
throw new Error(`Unexpected literal type: ${type}:${literal.value}`);
|
|
8338
|
+
}
|
|
8339
|
+
}
|
|
8340
|
+
function mcExprFromType(type) {
|
|
8341
|
+
switch (type.type) {
|
|
8342
|
+
case TypeTag.Null:
|
|
8343
|
+
return { type: "Literal", value: null, raw: "null" };
|
|
8344
|
+
case TypeTag.False:
|
|
8345
|
+
return { type: "Literal", value: false, raw: "false" };
|
|
8346
|
+
case TypeTag.True:
|
|
8347
|
+
return { type: "Literal", value: true, raw: "true" };
|
|
8348
|
+
case TypeTag.Number:
|
|
8349
|
+
return { type: "Literal", value: type.value, raw: `${type.value}` };
|
|
8350
|
+
case TypeTag.Long:
|
|
8351
|
+
return { type: "Literal", value: type.value, raw: `${type.value}l` };
|
|
8352
|
+
case TypeTag.Float: {
|
|
8353
|
+
let raw = type.value.toString();
|
|
8354
|
+
if (prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.test(raw)) {
|
|
8355
|
+
raw += "f";
|
|
8356
|
+
}
|
|
8357
|
+
else {
|
|
8358
|
+
const match = raw.match(/^(-)?(\d*)\.(\d+)(e\d+)?/);
|
|
8359
|
+
if (match && match[2].length + match[3].length > 9) {
|
|
8360
|
+
for (let l = 9 - match[2].length; l > 0; l--) {
|
|
8361
|
+
const s = `${match[1] || ""}${match[2]}.${match[3].substring(0, l)}${match[4] || ""}`;
|
|
8362
|
+
if (type.value !== roundToFloat(parseFloat(s)))
|
|
8363
|
+
break;
|
|
8364
|
+
raw = s;
|
|
8365
|
+
}
|
|
8366
|
+
}
|
|
8367
|
+
}
|
|
8368
|
+
return { type: "Literal", value: type.value, raw };
|
|
8369
|
+
}
|
|
8370
|
+
case TypeTag.Double:
|
|
8371
|
+
return { type: "Literal", value: type.value, raw: `${type.value}d` };
|
|
8372
|
+
case TypeTag.Char:
|
|
8373
|
+
return {
|
|
8374
|
+
type: "Literal",
|
|
8375
|
+
value: type.value,
|
|
8376
|
+
raw: `'${JSON.stringify(type.value).slice(1, -1)}'`,
|
|
8377
|
+
};
|
|
8378
|
+
case TypeTag.String:
|
|
8379
|
+
return {
|
|
8380
|
+
type: "Literal",
|
|
8381
|
+
value: type.value,
|
|
8382
|
+
raw: JSON.stringify(type.value),
|
|
8383
|
+
};
|
|
8384
|
+
case TypeTag.Enum:
|
|
8385
|
+
if (type.value.value && hasValue(type.value.value)) {
|
|
8386
|
+
const left = mcExprFromType(type.value.value);
|
|
8387
|
+
if (left) {
|
|
8388
|
+
return {
|
|
8389
|
+
type: "BinaryExpression",
|
|
8390
|
+
operator: "as",
|
|
8391
|
+
left,
|
|
8392
|
+
right: {
|
|
8393
|
+
type: "TypeSpecList",
|
|
8394
|
+
ts: [type.value.enum.fullName.slice(2)],
|
|
8395
|
+
},
|
|
8396
|
+
};
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
}
|
|
8400
|
+
return null;
|
|
8401
|
+
}
|
|
8402
|
+
/*
|
|
8403
|
+
* Cast one type to another, as might happen during
|
|
8404
|
+
* operator coercion. If the source type has a value,
|
|
8405
|
+
* try to preserve it.
|
|
8406
|
+
* eg in 1.0 + 2, the 2 will be converted to 2.0 before adding.
|
|
8407
|
+
* Note that many possible conversions simply can't happen.
|
|
8408
|
+
* eg Number can be converted to Long, Float or Double, but
|
|
8409
|
+
* Long, Float and Double never get converted to Number.
|
|
8410
|
+
*/
|
|
8411
|
+
function castType(type, target) {
|
|
8412
|
+
if (type.type === target)
|
|
8413
|
+
return cloneType(type);
|
|
8414
|
+
const result = { type: target };
|
|
8415
|
+
if (hasValue(type)) {
|
|
8416
|
+
if (isExact(result)) {
|
|
8417
|
+
switch (result.type) {
|
|
8418
|
+
case TypeTag.Null:
|
|
8419
|
+
case TypeTag.False:
|
|
8420
|
+
case TypeTag.True:
|
|
8421
|
+
case TypeTag.Number:
|
|
8422
|
+
break;
|
|
8423
|
+
case TypeTag.Long:
|
|
8424
|
+
if (type.type === TypeTag.Number) {
|
|
8425
|
+
result.value = BigInt(type.value);
|
|
8426
|
+
return result;
|
|
8427
|
+
}
|
|
8428
|
+
break;
|
|
8429
|
+
case TypeTag.Float:
|
|
8430
|
+
if (type.type === TypeTag.Number) {
|
|
8431
|
+
result.value = type.value;
|
|
8432
|
+
return result;
|
|
8433
|
+
}
|
|
8434
|
+
break;
|
|
8435
|
+
case TypeTag.Double:
|
|
8436
|
+
switch (type.type) {
|
|
8437
|
+
case TypeTag.Number:
|
|
8438
|
+
case TypeTag.Long:
|
|
8439
|
+
case TypeTag.Float:
|
|
8440
|
+
result.value = Number(type.value);
|
|
8441
|
+
return result;
|
|
8442
|
+
}
|
|
8443
|
+
break;
|
|
8444
|
+
case TypeTag.Char:
|
|
8445
|
+
switch (type.type) {
|
|
8446
|
+
case TypeTag.Number:
|
|
8447
|
+
case TypeTag.Long:
|
|
8448
|
+
result.value = String.fromCharCode(Number(type.value));
|
|
8449
|
+
return result;
|
|
8450
|
+
}
|
|
8451
|
+
break;
|
|
8452
|
+
case TypeTag.String:
|
|
8453
|
+
switch (type.type) {
|
|
8454
|
+
case TypeTag.Null:
|
|
8455
|
+
result.value = "null";
|
|
8456
|
+
return result;
|
|
8457
|
+
case TypeTag.False:
|
|
8458
|
+
result.value = "false";
|
|
8459
|
+
return result;
|
|
8460
|
+
case TypeTag.True:
|
|
8461
|
+
result.value = "true";
|
|
8462
|
+
return result;
|
|
8463
|
+
case TypeTag.Number:
|
|
8464
|
+
case TypeTag.Long:
|
|
8465
|
+
result.value = type.value.toString();
|
|
8466
|
+
return result;
|
|
8467
|
+
case TypeTag.Float:
|
|
8468
|
+
case TypeTag.Double:
|
|
8469
|
+
// Dont try to implement these, due to inconsistencies and bugs
|
|
8470
|
+
return result;
|
|
8471
|
+
case TypeTag.Char:
|
|
8472
|
+
result.value = type.value;
|
|
8473
|
+
return result;
|
|
8474
|
+
default:
|
|
8475
|
+
return result;
|
|
8476
|
+
}
|
|
8477
|
+
break;
|
|
8478
|
+
}
|
|
8479
|
+
throw new Error(`Trying to cast ${display(type)} to ${display(result)}`);
|
|
8480
|
+
}
|
|
8481
|
+
else if (result.type === TypeTag.Boolean) {
|
|
8482
|
+
// Number or Long operands to '&', '|', and '^' are coerced
|
|
8483
|
+
// to boolean if the other argument is boolean.
|
|
8484
|
+
if (type.type & (TypeTag.Number | TypeTag.Long)) {
|
|
8485
|
+
result.type = type.value == 0 ? TypeTag.False : TypeTag.True;
|
|
8486
|
+
return result;
|
|
8487
|
+
}
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
return result;
|
|
8491
|
+
}
|
|
8492
|
+
/*
|
|
8493
|
+
* Anything consisting of solely these types is definitely true
|
|
8494
|
+
*/
|
|
8495
|
+
const TruthyTypes = TypeTag.True |
|
|
8496
|
+
TypeTag.Object |
|
|
8497
|
+
TypeTag.Module |
|
|
8498
|
+
TypeTag.Class |
|
|
8499
|
+
TypeTag.Function;
|
|
8500
|
+
function mustBeTrue(arg) {
|
|
8501
|
+
return (((arg.type === TypeTag.Number || arg.type === TypeTag.Long) &&
|
|
8502
|
+
arg.value != null &&
|
|
8503
|
+
arg.value != 0) ||
|
|
8504
|
+
((arg.type & TruthyTypes) != 0 && (arg.type & ~TruthyTypes) == 0));
|
|
8505
|
+
}
|
|
8506
|
+
function mustBeFalse(arg) {
|
|
8507
|
+
return (arg.type === TypeTag.Null ||
|
|
8508
|
+
arg.type === TypeTag.False ||
|
|
8509
|
+
((arg.type === TypeTag.Number || arg.type === TypeTag.Long) &&
|
|
8510
|
+
arg.value != null &&
|
|
8511
|
+
arg.value == 0));
|
|
8512
|
+
}
|
|
8513
|
+
function display(type) {
|
|
8514
|
+
const names = (v, fn) => (0,external_util_cjs_namespaceObject.map)(v, fn)
|
|
8515
|
+
.sort()
|
|
8516
|
+
.filter((s, i, arr) => !i || s !== arr[i - 1])
|
|
8517
|
+
.join(" or ");
|
|
8518
|
+
const parts = [];
|
|
8519
|
+
const displayOne = (bit, value) => {
|
|
8520
|
+
switch (bit) {
|
|
8521
|
+
case TypeTag.Null:
|
|
8522
|
+
case TypeTag.False:
|
|
8523
|
+
case TypeTag.True:
|
|
8524
|
+
throw new Error("Unexpected value for SingletonTypeTag");
|
|
8525
|
+
case TypeTag.Number:
|
|
8526
|
+
case TypeTag.Long:
|
|
8527
|
+
case TypeTag.Float:
|
|
8528
|
+
case TypeTag.Double:
|
|
8529
|
+
return value.toString();
|
|
8530
|
+
case TypeTag.Char:
|
|
8531
|
+
return `'${JSON.stringify(value).slice(1, -1)}'`;
|
|
8532
|
+
case TypeTag.String:
|
|
8533
|
+
return JSON.stringify(type.value);
|
|
8534
|
+
case TypeTag.Array:
|
|
8535
|
+
return display(value);
|
|
8536
|
+
case TypeTag.Dictionary:
|
|
8537
|
+
return `${display(value.key)}, ${display(value.value)}`;
|
|
8538
|
+
case TypeTag.Module:
|
|
8539
|
+
case TypeTag.Function:
|
|
8540
|
+
case TypeTag.Class:
|
|
8541
|
+
case TypeTag.Typedef:
|
|
8542
|
+
return names(value, (v) => v.fullName.slice(2));
|
|
8543
|
+
case TypeTag.Object: {
|
|
8544
|
+
const klass = value.klass;
|
|
8545
|
+
if (!klass.value)
|
|
8546
|
+
return undefined;
|
|
8547
|
+
const obj = value.obj;
|
|
8548
|
+
const ret = displayOne(TypeTag.Class, klass.value);
|
|
8549
|
+
return obj
|
|
8550
|
+
? `${ret}<{${Object.entries(obj)
|
|
8551
|
+
.map(([key, value]) => `${key}: ${display(value)}`)
|
|
8552
|
+
.join(", ")}}>`
|
|
8553
|
+
: ret;
|
|
8554
|
+
}
|
|
8555
|
+
case TypeTag.Enum: {
|
|
8556
|
+
const v = value;
|
|
8557
|
+
return v.value
|
|
8558
|
+
? `${display(v.value)} as ${v.enum.fullName.slice(2)}`
|
|
8559
|
+
: v.enum.fullName.slice(2);
|
|
8560
|
+
}
|
|
8561
|
+
case TypeTag.Symbol:
|
|
8562
|
+
return `:${value}`;
|
|
8563
|
+
default:
|
|
8564
|
+
throw new Error(`Unexpected type tag '${bit}'`);
|
|
8565
|
+
}
|
|
8566
|
+
};
|
|
8567
|
+
let bits = type.type;
|
|
8568
|
+
if (!bits)
|
|
8569
|
+
return "Never";
|
|
8570
|
+
if (bits === TypeTag.Any && type.value == null) {
|
|
8571
|
+
return "Any";
|
|
8572
|
+
}
|
|
8573
|
+
while (bits) {
|
|
8574
|
+
const next = bits & (bits - 1);
|
|
8575
|
+
const bit = bits - next;
|
|
8576
|
+
if (bit === TypeTag.False && next & TypeTag.True) {
|
|
8577
|
+
parts.push("Boolean");
|
|
8578
|
+
bits = next - TypeTag.True;
|
|
8579
|
+
continue;
|
|
8580
|
+
}
|
|
8581
|
+
const name = TypeTag[bit];
|
|
8582
|
+
const value = getUnionComponent(type, bit);
|
|
8583
|
+
const valueStr = value != null && displayOne(bit, value);
|
|
8584
|
+
if (!valueStr) {
|
|
8585
|
+
parts.push(name);
|
|
8586
|
+
}
|
|
8587
|
+
else if (bit &
|
|
8588
|
+
(TypeTag.Object |
|
|
8589
|
+
TypeTag.Enum |
|
|
8590
|
+
TypeTag.Typedef |
|
|
8591
|
+
TypeTag.Symbol |
|
|
8592
|
+
TypeTag.String)) {
|
|
8593
|
+
parts.push(valueStr);
|
|
8594
|
+
}
|
|
8595
|
+
else {
|
|
8596
|
+
parts.push(`${name}<${valueStr}${valueStr.endsWith(">") ? " " : ""}>`);
|
|
8597
|
+
}
|
|
8598
|
+
bits = next;
|
|
8599
|
+
}
|
|
8600
|
+
return parts.join(" or ");
|
|
8601
|
+
}
|
|
8602
|
+
function hasUnionData(tag) {
|
|
8603
|
+
tag &= UnionDataTypeTagsConst;
|
|
8604
|
+
return (tag & (tag - 1)) != 0;
|
|
8605
|
+
}
|
|
8606
|
+
function getObjectValue(t) {
|
|
8607
|
+
if (!(t.type & TypeTag.Object) || t.value == null)
|
|
8608
|
+
return null;
|
|
8609
|
+
if (hasUnionData(t.type)) {
|
|
8610
|
+
return t.value[TypeTag.Object];
|
|
8611
|
+
}
|
|
8612
|
+
return t.value;
|
|
8613
|
+
}
|
|
8614
|
+
function forEachUnionComponent(v, bits, fn) {
|
|
8615
|
+
// never iterate the singleton bits, because they don't have data
|
|
8616
|
+
bits &= ~SingleTonTypeTagsConst;
|
|
8617
|
+
if (!bits)
|
|
8618
|
+
return;
|
|
8619
|
+
if (v.type & UnionDataTypeTagsConst) {
|
|
8620
|
+
// Don't iterate the value type bits if any union bit is set
|
|
8621
|
+
bits &= ~ValueTypeTagsConst;
|
|
8622
|
+
}
|
|
8623
|
+
else if (bits & (bits - 1)) {
|
|
8624
|
+
// More than one ValueTypeTagsConst bit set, so there's
|
|
8625
|
+
// no data.
|
|
8626
|
+
return;
|
|
8627
|
+
}
|
|
8628
|
+
const hasUnion = hasUnionData(v.type);
|
|
8629
|
+
const unionData = v.value;
|
|
8630
|
+
do {
|
|
8631
|
+
const next = bits & (bits - 1);
|
|
8632
|
+
const bit = bits - next;
|
|
8633
|
+
const data = hasUnion
|
|
8634
|
+
? unionData[bit]
|
|
8635
|
+
: bit & v.type
|
|
8636
|
+
? v.value
|
|
8637
|
+
: null;
|
|
8638
|
+
if (fn(bit, data) === false)
|
|
8639
|
+
break;
|
|
8640
|
+
bits = next;
|
|
8641
|
+
} while (bits);
|
|
8642
|
+
}
|
|
8643
|
+
function getUnionComponent(v, tag) {
|
|
8644
|
+
if (v.value == null)
|
|
8645
|
+
return null;
|
|
8646
|
+
let bits = v.type & ~SingleTonTypeTagsConst;
|
|
8647
|
+
if (!bits)
|
|
8648
|
+
return null;
|
|
8649
|
+
if (bits & (bits - 1)) {
|
|
8650
|
+
bits &= UnionDataTypeTagsConst;
|
|
8651
|
+
if (!bits) {
|
|
8652
|
+
throw new Error(`Non-exact type had no union bits set`);
|
|
8653
|
+
}
|
|
8654
|
+
}
|
|
8655
|
+
if (bits === tag) {
|
|
8656
|
+
return v.value;
|
|
8657
|
+
}
|
|
8658
|
+
else if (bits & tag) {
|
|
8659
|
+
const unionData = v.value;
|
|
8660
|
+
return unionData[tag] || null;
|
|
8661
|
+
}
|
|
8662
|
+
return null;
|
|
8663
|
+
}
|
|
8664
|
+
|
|
8665
|
+
;// CONCATENATED MODULE: ./src/type-flow/interp-binary.ts
|
|
8666
|
+
|
|
8667
|
+
|
|
8668
|
+
|
|
8669
|
+
/*
|
|
8670
|
+
* Compute the possible type tags of the result of an arithmetic
|
|
8671
|
+
* operation on left and right.
|
|
8672
|
+
*
|
|
8673
|
+
* allowed gives the set of allowed input types.
|
|
8674
|
+
*
|
|
8675
|
+
* + => Number | Long | Float | Double | String | Char | Null
|
|
8676
|
+
* - => Number | Long | Float | Double
|
|
8677
|
+
*/
|
|
8678
|
+
function common_types(left, right, allowed) {
|
|
8679
|
+
const types = left.type | right.type;
|
|
8680
|
+
let mask = TypeTag.True |
|
|
8681
|
+
TypeTag.False |
|
|
8682
|
+
TypeTag.Number |
|
|
8683
|
+
TypeTag.Long |
|
|
8684
|
+
TypeTag.Float |
|
|
8685
|
+
TypeTag.Double;
|
|
8686
|
+
if (types & allowed & TypeTag.String) {
|
|
8687
|
+
mask |=
|
|
8688
|
+
TypeTag.String |
|
|
8689
|
+
TypeTag.Char |
|
|
8690
|
+
TypeTag.Null |
|
|
8691
|
+
TypeTag.Dictionary |
|
|
8692
|
+
TypeTag.Array;
|
|
8693
|
+
}
|
|
8694
|
+
else if (types & allowed & TypeTag.Char) {
|
|
8695
|
+
mask |= TypeTag.Char;
|
|
8696
|
+
}
|
|
8697
|
+
mask &= allowed;
|
|
8698
|
+
const lt = left.type & mask;
|
|
8699
|
+
const rt = right.type & mask;
|
|
8700
|
+
let result = lt & TypeTag.String;
|
|
8701
|
+
if (lt & TypeTag.Null)
|
|
8702
|
+
result |= rt & TypeTag.String;
|
|
8703
|
+
if (lt & TypeTag.Boolean) {
|
|
8704
|
+
result |=
|
|
8705
|
+
(rt & TypeTag.Boolean) | TypeTag.Number | TypeTag.Long && TypeTag.Boolean;
|
|
8706
|
+
}
|
|
8707
|
+
if (lt & TypeTag.Number) {
|
|
8708
|
+
result |=
|
|
8709
|
+
rt & TypeTag.Boolean ? TypeTag.Boolean : rt & ~SingleTonTypeTagsConst;
|
|
8710
|
+
}
|
|
8711
|
+
if (lt & TypeTag.Long) {
|
|
8712
|
+
if (rt & TypeTag.Boolean) {
|
|
8713
|
+
result |= TypeTag.Boolean;
|
|
8714
|
+
}
|
|
8715
|
+
else {
|
|
8716
|
+
if (rt & (TypeTag.Number | TypeTag.Long))
|
|
8717
|
+
result |= TypeTag.Long;
|
|
8718
|
+
if (rt & (TypeTag.Float | TypeTag.Double))
|
|
8719
|
+
result |= TypeTag.Double;
|
|
8720
|
+
result |= rt & (TypeTag.String | TypeTag.Char);
|
|
8721
|
+
}
|
|
8722
|
+
}
|
|
8723
|
+
if (lt & TypeTag.Float) {
|
|
8724
|
+
if (rt & (TypeTag.Number | TypeTag.Float))
|
|
8725
|
+
result |= TypeTag.Float;
|
|
8726
|
+
if (rt & (TypeTag.Long | TypeTag.Double))
|
|
8727
|
+
result |= TypeTag.Double;
|
|
8728
|
+
result |= rt & TypeTag.String;
|
|
8729
|
+
}
|
|
8730
|
+
if (lt & TypeTag.Double) {
|
|
8731
|
+
if (rt & (TypeTag.Number | TypeTag.Long | TypeTag.Float | TypeTag.Double)) {
|
|
8732
|
+
result |= TypeTag.Double;
|
|
8733
|
+
}
|
|
8734
|
+
result |= rt & TypeTag.String;
|
|
8735
|
+
}
|
|
8736
|
+
if (lt & TypeTag.Char) {
|
|
8737
|
+
if (rt & (TypeTag.Number | TypeTag.Long)) {
|
|
8738
|
+
result |= TypeTag.Char;
|
|
8739
|
+
}
|
|
8740
|
+
if (rt & (TypeTag.Char | TypeTag.String)) {
|
|
8741
|
+
result |= TypeTag.String;
|
|
8742
|
+
}
|
|
8743
|
+
}
|
|
8744
|
+
return {
|
|
8745
|
+
tag: result,
|
|
8746
|
+
castArgs: !(result & TypeTag.Char) ? result : false,
|
|
8747
|
+
};
|
|
8748
|
+
}
|
|
8749
|
+
function compare_types(left, right) {
|
|
8750
|
+
const ret = common_types(left, right, TypeTag.Number | TypeTag.Long | TypeTag.Float | TypeTag.Double);
|
|
8751
|
+
return { tag: TypeTag.Boolean, castArgs: ret.castArgs };
|
|
8752
|
+
}
|
|
8753
|
+
function equalsCheck(left, right) {
|
|
8754
|
+
// compare numbers for equality without regard for type;
|
|
8755
|
+
// otherwise if its Number vs Char, compare the char-code with the number;
|
|
8756
|
+
// otherwise if its Number vs Boolean, 1/0 compare equal to true/false
|
|
8757
|
+
// otherwise if the types are different, the result is
|
|
8758
|
+
// - unknown if its Object vs any normal type (because eg Number is also Object)
|
|
8759
|
+
// - currently unknown if its Object vs Null (because we can't trust api.mir to
|
|
8760
|
+
// always include Null in the possible return types)
|
|
8761
|
+
// otherwise if its a singleton type (Null, True, False)
|
|
8762
|
+
// the result is true;
|
|
8763
|
+
// otherwise if its a Char or Symbol, compare for equality
|
|
8764
|
+
// otherwise its unknown (we don't track object identity).
|
|
8765
|
+
// Note that each type can only have a single bit set. This is important!
|
|
8766
|
+
return left.type & TypeTag.Numeric && right.type & TypeTag.Numeric
|
|
8767
|
+
? left.value == right.value
|
|
8768
|
+
: (left.type | right.type) == (TypeTag.Number | TypeTag.Char)
|
|
8769
|
+
? // Char vs Number is true iff the number is the char-code of the char
|
|
8770
|
+
left.type === TypeTag.Char
|
|
8771
|
+
? left.value.charCodeAt(0) === right.value
|
|
8772
|
+
: left.value === right.value.charCodeAt(0)
|
|
8773
|
+
: left.type == TypeTag.Number && right.type & TypeTag.Boolean
|
|
8774
|
+
? left.value == (right.value ? 1 : 0)
|
|
8775
|
+
: right.type == TypeTag.Number && left.type & TypeTag.Boolean
|
|
8776
|
+
? right.value == (left.value ? 1 : 0)
|
|
8777
|
+
: left.type !== right.type
|
|
8778
|
+
? ((left.type | right.type) &
|
|
8779
|
+
(TypeTag.Object |
|
|
8780
|
+
TypeTag.Module |
|
|
8781
|
+
TypeTag.Function |
|
|
8782
|
+
TypeTag.Class)) ===
|
|
8783
|
+
TypeTag.Object
|
|
8784
|
+
? undefined
|
|
8785
|
+
: false
|
|
8786
|
+
: left.type === TypeTag.Char || left.type === TypeTag.Symbol
|
|
8787
|
+
? left.value === right.value
|
|
8788
|
+
: isSingleton(left)
|
|
8789
|
+
? true
|
|
8790
|
+
: undefined;
|
|
8791
|
+
}
|
|
8792
|
+
let operators = null;
|
|
8793
|
+
function evaluateBinaryTypes(op, left, right) {
|
|
8794
|
+
if (!operators) {
|
|
8795
|
+
operators = {
|
|
8796
|
+
"+": {
|
|
8797
|
+
allowed: TypeTag.Number |
|
|
8798
|
+
TypeTag.Long |
|
|
8799
|
+
TypeTag.Float |
|
|
8800
|
+
TypeTag.Double |
|
|
8801
|
+
TypeTag.String |
|
|
8802
|
+
TypeTag.Char |
|
|
8803
|
+
TypeTag.Array |
|
|
8804
|
+
TypeTag.Dictionary |
|
|
8805
|
+
TypeTag.Null,
|
|
8806
|
+
typeFn: common_types,
|
|
8807
|
+
valueFn: (left, right) => left.type === TypeTag.Char &&
|
|
8808
|
+
right.type & (TypeTag.Number | TypeTag.Long)
|
|
8809
|
+
? {
|
|
8810
|
+
type: TypeTag.Char,
|
|
8811
|
+
value: String.fromCharCode(left.value.charCodeAt(0) + Number(right.value)),
|
|
8812
|
+
}
|
|
8813
|
+
: left.type & (TypeTag.Number | TypeTag.Long) &&
|
|
8814
|
+
right.type === TypeTag.Char
|
|
8815
|
+
? {
|
|
8816
|
+
type: TypeTag.Char,
|
|
8817
|
+
value: String.fromCharCode(right.value.charCodeAt(0) + Number(left.value)),
|
|
8818
|
+
}
|
|
8819
|
+
: {
|
|
8820
|
+
type: left.type,
|
|
8821
|
+
value: left.value + right.value,
|
|
8822
|
+
},
|
|
8823
|
+
},
|
|
8824
|
+
"-": {
|
|
8825
|
+
allowed: TypeTag.Number | TypeTag.Long | TypeTag.Float | TypeTag.Double,
|
|
8826
|
+
typeFn: common_types,
|
|
8827
|
+
valueFn: (left, right) => ({
|
|
8828
|
+
type: left.type,
|
|
8829
|
+
value: left.value - right.value,
|
|
8830
|
+
}),
|
|
8831
|
+
},
|
|
8832
|
+
"*": {
|
|
8833
|
+
allowed: TypeTag.Number | TypeTag.Long | TypeTag.Float | TypeTag.Double,
|
|
8834
|
+
typeFn: common_types,
|
|
8835
|
+
valueFn: (left, right) => ({
|
|
8836
|
+
type: left.type,
|
|
8837
|
+
value: left.value * right.value,
|
|
8838
|
+
}),
|
|
8839
|
+
},
|
|
8840
|
+
"/": {
|
|
8841
|
+
allowed: TypeTag.Number | TypeTag.Long | TypeTag.Float | TypeTag.Double,
|
|
8842
|
+
typeFn: common_types,
|
|
8843
|
+
valueFn: (left, right) => right.value == 0 // "==" because it could be a bigint
|
|
8844
|
+
? { type: left.type }
|
|
8845
|
+
: left.type === TypeTag.Number
|
|
8846
|
+
? {
|
|
8847
|
+
type: left.type,
|
|
8848
|
+
value: Number(BigInt(left.value) / BigInt(right.value)),
|
|
8849
|
+
}
|
|
8850
|
+
: {
|
|
8851
|
+
type: left.type,
|
|
8852
|
+
value: left.value / right.value,
|
|
8853
|
+
},
|
|
8854
|
+
},
|
|
8855
|
+
"%": {
|
|
8856
|
+
allowed: TypeTag.Number | TypeTag.Long,
|
|
8857
|
+
typeFn: common_types,
|
|
8858
|
+
valueFn: (left, right) => right.value == 0 // "==" because it could be a bigint
|
|
8859
|
+
? { type: left.type }
|
|
8860
|
+
: {
|
|
8861
|
+
type: left.type,
|
|
8862
|
+
value: left.value % right.value,
|
|
8863
|
+
},
|
|
8864
|
+
},
|
|
8865
|
+
"&": {
|
|
8866
|
+
allowed: TypeTag.Boolean | TypeTag.Number | TypeTag.Long,
|
|
8867
|
+
typeFn: common_types,
|
|
8868
|
+
valueFn: (left, right) => left.type & TypeTag.Boolean
|
|
8869
|
+
? {
|
|
8870
|
+
type: left.type === TypeTag.True && right.type === TypeTag.True
|
|
8871
|
+
? TypeTag.True
|
|
8872
|
+
: TypeTag.False,
|
|
8873
|
+
}
|
|
8874
|
+
: {
|
|
8875
|
+
type: left.type,
|
|
8876
|
+
value: left.value & right.value,
|
|
8877
|
+
},
|
|
8878
|
+
},
|
|
8879
|
+
"|": {
|
|
8880
|
+
allowed: TypeTag.Boolean | TypeTag.Number | TypeTag.Long,
|
|
8881
|
+
typeFn: common_types,
|
|
8882
|
+
valueFn: (left, right) => left.type & TypeTag.Boolean
|
|
8883
|
+
? {
|
|
8884
|
+
type: left.type === TypeTag.True || right.type === TypeTag.True
|
|
8885
|
+
? TypeTag.True
|
|
8886
|
+
: TypeTag.False,
|
|
8887
|
+
}
|
|
8888
|
+
: {
|
|
8889
|
+
type: left.type,
|
|
8890
|
+
value: left.value | right.value,
|
|
8891
|
+
},
|
|
8892
|
+
},
|
|
8893
|
+
"^": {
|
|
8894
|
+
allowed: TypeTag.Boolean | TypeTag.Number | TypeTag.Long,
|
|
8895
|
+
typeFn: common_types,
|
|
8896
|
+
valueFn: (left, right) => left.type & TypeTag.Boolean
|
|
8897
|
+
? {
|
|
8898
|
+
type: (left.type === TypeTag.True) !== (right.type === TypeTag.True)
|
|
8899
|
+
? TypeTag.True
|
|
8900
|
+
: TypeTag.False,
|
|
8901
|
+
}
|
|
8902
|
+
: {
|
|
8903
|
+
type: left.type,
|
|
8904
|
+
value: left.value ^ right.value,
|
|
8905
|
+
},
|
|
8906
|
+
},
|
|
8907
|
+
"<<": {
|
|
8908
|
+
allowed: TypeTag.Number | TypeTag.Long,
|
|
8909
|
+
typeFn: common_types,
|
|
8910
|
+
valueFn: (left, right) => left.type === TypeTag.Long
|
|
8911
|
+
? {
|
|
8912
|
+
type: TypeTag.Long,
|
|
8913
|
+
value: left.value << right.value,
|
|
8914
|
+
}
|
|
8915
|
+
: {
|
|
8916
|
+
type: TypeTag.Number,
|
|
8917
|
+
value: left.value << (right.value & 63),
|
|
8918
|
+
},
|
|
8919
|
+
},
|
|
8920
|
+
">>": {
|
|
8921
|
+
allowed: TypeTag.Number | TypeTag.Long,
|
|
8922
|
+
typeFn: common_types,
|
|
8923
|
+
valueFn: (left, right) => left.type === TypeTag.Long
|
|
8924
|
+
? {
|
|
8925
|
+
type: TypeTag.Long,
|
|
8926
|
+
value: left.value >> right.value,
|
|
8927
|
+
}
|
|
8928
|
+
: {
|
|
8929
|
+
type: TypeTag.Number,
|
|
8930
|
+
value: left.value >> (right.value & 63),
|
|
8931
|
+
},
|
|
8932
|
+
},
|
|
8933
|
+
"==": {
|
|
8934
|
+
allowed: TypeTag.Any,
|
|
8935
|
+
typeFn: () => ({
|
|
8936
|
+
tag: TypeTag.Boolean,
|
|
8937
|
+
castArgs: false,
|
|
8938
|
+
}),
|
|
8939
|
+
valueFn: (left, right) => {
|
|
8940
|
+
const result = equalsCheck(left, right);
|
|
8941
|
+
return result === undefined
|
|
8942
|
+
? result
|
|
8943
|
+
: { type: result ? TypeTag.True : TypeTag.False };
|
|
8944
|
+
},
|
|
8945
|
+
},
|
|
8946
|
+
"!=": {
|
|
8947
|
+
allowed: TypeTag.Any,
|
|
8948
|
+
typeFn: () => ({
|
|
8949
|
+
tag: TypeTag.Boolean,
|
|
8950
|
+
castArgs: false,
|
|
8951
|
+
}),
|
|
8952
|
+
valueFn: (left, right) => {
|
|
8953
|
+
const result = equalsCheck(left, right);
|
|
8954
|
+
return result === undefined
|
|
8955
|
+
? result
|
|
8956
|
+
: { type: result ? TypeTag.False : TypeTag.True };
|
|
8957
|
+
},
|
|
8958
|
+
},
|
|
8959
|
+
"<=": {
|
|
8960
|
+
allowed: TypeTag.Any,
|
|
8961
|
+
typeFn: compare_types,
|
|
8962
|
+
valueFn: (left, right) => ({
|
|
8963
|
+
type: left.value <= right.value
|
|
8964
|
+
? TypeTag.True
|
|
8965
|
+
: TypeTag.False,
|
|
8966
|
+
}),
|
|
8967
|
+
},
|
|
8968
|
+
">=": {
|
|
8969
|
+
allowed: TypeTag.Any,
|
|
8970
|
+
typeFn: compare_types,
|
|
8971
|
+
valueFn: (left, right) => ({
|
|
8972
|
+
type: left.value >= right.value
|
|
8973
|
+
? TypeTag.True
|
|
8974
|
+
: TypeTag.False,
|
|
8975
|
+
}),
|
|
8976
|
+
},
|
|
8977
|
+
"<": {
|
|
8978
|
+
allowed: TypeTag.Any,
|
|
8979
|
+
typeFn: compare_types,
|
|
8980
|
+
valueFn: (left, right) => ({
|
|
8981
|
+
type: left.value < right.value
|
|
8982
|
+
? TypeTag.True
|
|
8983
|
+
: TypeTag.False,
|
|
8984
|
+
}),
|
|
8985
|
+
},
|
|
8986
|
+
">": {
|
|
8987
|
+
allowed: TypeTag.Any,
|
|
8988
|
+
typeFn: compare_types,
|
|
8989
|
+
valueFn: (left, right) => ({
|
|
8990
|
+
type: left.value > right.value
|
|
8991
|
+
? TypeTag.True
|
|
8992
|
+
: TypeTag.False,
|
|
8993
|
+
}),
|
|
8994
|
+
},
|
|
8995
|
+
instanceof: {
|
|
8996
|
+
allowed: TypeTag.Any,
|
|
8997
|
+
typeFn: () => ({
|
|
8998
|
+
tag: TypeTag.Boolean,
|
|
8999
|
+
castArgs: false,
|
|
9000
|
+
}),
|
|
9001
|
+
valueFn: (_left, _right) => undefined,
|
|
9002
|
+
},
|
|
9003
|
+
has: {
|
|
9004
|
+
allowed: TypeTag.Any,
|
|
9005
|
+
typeFn: () => ({
|
|
9006
|
+
tag: TypeTag.Boolean,
|
|
9007
|
+
castArgs: false,
|
|
9008
|
+
}),
|
|
9009
|
+
valueFn: (_left, _right) => undefined,
|
|
9010
|
+
},
|
|
9011
|
+
};
|
|
9012
|
+
}
|
|
9013
|
+
const info = operators[op];
|
|
9014
|
+
if (!info)
|
|
9015
|
+
return { type: TypeTag.Any };
|
|
9016
|
+
const { tag, castArgs } = info.typeFn(left, right, info.allowed);
|
|
9017
|
+
const result_type = { type: tag };
|
|
9018
|
+
if (isExact(result_type) && castArgs !== false) {
|
|
9019
|
+
left = castType(left, castArgs);
|
|
9020
|
+
right = castType(right, castArgs);
|
|
9021
|
+
}
|
|
9022
|
+
if (hasValue(left) && hasValue(right)) {
|
|
9023
|
+
const value = info.valueFn(left, right);
|
|
9024
|
+
if (value) {
|
|
9025
|
+
if (value.type === TypeTag.Float && value.value != null) {
|
|
9026
|
+
value.value = roundToFloat(value.value);
|
|
9027
|
+
}
|
|
9028
|
+
return value;
|
|
9029
|
+
}
|
|
9030
|
+
}
|
|
9031
|
+
return result_type;
|
|
9032
|
+
}
|
|
9033
|
+
function evaluateLogicalTypes(op, left, right) {
|
|
9034
|
+
switch (op) {
|
|
9035
|
+
case "&&":
|
|
9036
|
+
case "and":
|
|
9037
|
+
if (mustBeFalse(left)) {
|
|
9038
|
+
return left;
|
|
9039
|
+
}
|
|
9040
|
+
else {
|
|
9041
|
+
const result = evaluateBinaryTypes("&", left, right);
|
|
9042
|
+
if ((left.type & TypeTag.Null) !== 0) {
|
|
9043
|
+
unionInto(result, { type: TypeTag.Null });
|
|
9044
|
+
}
|
|
9045
|
+
return result;
|
|
9046
|
+
}
|
|
9047
|
+
case "||":
|
|
9048
|
+
case "or":
|
|
9049
|
+
if (mustBeTrue(left)) {
|
|
9050
|
+
return left;
|
|
9051
|
+
}
|
|
9052
|
+
else {
|
|
9053
|
+
const result = evaluateBinaryTypes("|", left, right);
|
|
9054
|
+
if ((left.type & TruthyTypes) !== 0) {
|
|
9055
|
+
unionInto(result, { type: left.type & TruthyTypes });
|
|
9056
|
+
}
|
|
9057
|
+
return result;
|
|
9058
|
+
}
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
9061
|
+
|
|
9062
|
+
;// CONCATENATED MODULE: ./src/type-flow/interp-call.ts
|
|
9063
|
+
|
|
9064
|
+
|
|
9065
|
+
|
|
9066
|
+
function evaluateCall(state, node, callee, _args) {
|
|
9067
|
+
while (!hasValue(callee) || callee.type !== TypeTag.Function) {
|
|
9068
|
+
const name = node.callee.type === "Identifier"
|
|
9069
|
+
? node.callee
|
|
9070
|
+
: node.callee.type === "MemberExpression" && !node.callee.computed
|
|
9071
|
+
? node.callee.property
|
|
9072
|
+
: null;
|
|
9073
|
+
if (name) {
|
|
9074
|
+
const decls = state.allFunctions[name.name];
|
|
9075
|
+
if (decls) {
|
|
9076
|
+
callee = typeFromTypeStateNodes(state, decls);
|
|
9077
|
+
if (hasValue(callee) && callee.type === TypeTag.Function) {
|
|
9078
|
+
break;
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
return { type: TypeTag.Any };
|
|
9083
|
+
}
|
|
9084
|
+
return (0,external_util_cjs_namespaceObject.reduce)(callee.value, (result, cur) => {
|
|
9085
|
+
if (cur.node.returnType) {
|
|
9086
|
+
const returnType = typeFromTypespec(state, cur.node.returnType.argument, cur.stack);
|
|
9087
|
+
unionInto(result, returnType);
|
|
9088
|
+
}
|
|
9089
|
+
else {
|
|
9090
|
+
result.type = TypeTag.Any;
|
|
9091
|
+
delete result.value;
|
|
9092
|
+
}
|
|
9093
|
+
return result;
|
|
9094
|
+
}, { type: TypeTag.Never });
|
|
9095
|
+
}
|
|
9096
|
+
|
|
9097
|
+
;// CONCATENATED MODULE: ./src/type-flow/interp.ts
|
|
9098
|
+
|
|
9099
|
+
|
|
9100
|
+
|
|
9101
|
+
|
|
9102
|
+
|
|
9103
|
+
|
|
9104
|
+
|
|
9105
|
+
|
|
9106
|
+
function popIstate(istate, node) {
|
|
9107
|
+
const item = istate.stack.pop();
|
|
9108
|
+
if (!item) {
|
|
9109
|
+
throw new Error("Unbalanced stack!");
|
|
9110
|
+
}
|
|
9111
|
+
if (item.node !== node) {
|
|
9112
|
+
throw new Error("Stack mismatch");
|
|
9113
|
+
}
|
|
9114
|
+
return item;
|
|
9115
|
+
}
|
|
9116
|
+
function evaluateExpr(state, expr, typeMap) {
|
|
9117
|
+
return interp_evaluate({ state, stack: [], typeMap }, expr);
|
|
9118
|
+
}
|
|
9119
|
+
function interp_evaluate(istate, node) {
|
|
9120
|
+
let skipNode = null;
|
|
9121
|
+
const post = (node) => {
|
|
9122
|
+
if (istate.pre && node !== skipNode) {
|
|
9123
|
+
const rep = istate.pre(node);
|
|
9124
|
+
if (rep)
|
|
9125
|
+
return rep;
|
|
9126
|
+
}
|
|
9127
|
+
evaluateNode(istate, node);
|
|
9128
|
+
if (skipNode === node) {
|
|
9129
|
+
skipNode = null;
|
|
9130
|
+
return null;
|
|
9131
|
+
}
|
|
9132
|
+
return istate.post ? istate.post(node) : null;
|
|
9133
|
+
};
|
|
9134
|
+
const pre = (node) => {
|
|
9135
|
+
switch (node.type) {
|
|
9136
|
+
case "MemberExpression":
|
|
9137
|
+
if ((0,external_api_cjs_namespaceObject.isLookupCandidate)(node)) {
|
|
9138
|
+
return ["object"];
|
|
9139
|
+
}
|
|
9140
|
+
break;
|
|
9141
|
+
case "BinaryExpression":
|
|
9142
|
+
if (node.operator === "as") {
|
|
9143
|
+
return ["left"];
|
|
9144
|
+
}
|
|
9145
|
+
break;
|
|
9146
|
+
case "UnaryExpression":
|
|
9147
|
+
if (node.operator === ":") {
|
|
9148
|
+
return [];
|
|
9149
|
+
}
|
|
9150
|
+
break;
|
|
9151
|
+
case "AttributeList":
|
|
9152
|
+
return [];
|
|
9153
|
+
case "AssignmentExpression":
|
|
9154
|
+
skipNode = node.left;
|
|
9155
|
+
break;
|
|
9156
|
+
case "UpdateExpression":
|
|
9157
|
+
skipNode = node.argument;
|
|
9158
|
+
break;
|
|
9159
|
+
case "SizedArrayExpression":
|
|
9160
|
+
return ["size"];
|
|
9161
|
+
case "VariableDeclarator":
|
|
9162
|
+
return ["init"];
|
|
9163
|
+
case "CatchClause":
|
|
9164
|
+
return ["body"];
|
|
9165
|
+
}
|
|
9166
|
+
return null;
|
|
9167
|
+
};
|
|
9168
|
+
traverseAst(node, pre, post);
|
|
9169
|
+
const ret = istate.stack.pop();
|
|
9170
|
+
if (isExpression(node)) {
|
|
9171
|
+
if (!ret || node !== ret.node) {
|
|
9172
|
+
throw new Error("evaluate failed to produce a value for an expression");
|
|
9173
|
+
}
|
|
9174
|
+
}
|
|
9175
|
+
return ret;
|
|
9176
|
+
}
|
|
9177
|
+
function evaluateUnaryTypes(op, argument) {
|
|
9178
|
+
switch (op) {
|
|
9179
|
+
case "+":
|
|
9180
|
+
return argument;
|
|
9181
|
+
case "-":
|
|
9182
|
+
return evaluateBinaryTypes("-", { type: TypeTag.Number, value: 0 }, argument);
|
|
9183
|
+
case "!":
|
|
9184
|
+
case "~":
|
|
9185
|
+
if (hasValue(argument)) {
|
|
9186
|
+
const left = argument.type & TypeTag.Boolean
|
|
9187
|
+
? { type: TypeTag.True }
|
|
9188
|
+
: { type: TypeTag.Number, value: -1 };
|
|
9189
|
+
return evaluateBinaryTypes("^", left, argument);
|
|
9190
|
+
}
|
|
9191
|
+
return {
|
|
9192
|
+
type: argument.type & (TypeTag.Boolean | TypeTag.Number | TypeTag.Long),
|
|
9193
|
+
};
|
|
9194
|
+
}
|
|
9195
|
+
throw new Error(`Unexpected unary operator ${op}`);
|
|
9196
|
+
}
|
|
9197
|
+
/*
|
|
9198
|
+
* When an enumeration constant, or a constant cast to an
|
|
9199
|
+
* enum type is used in an arithmetic context, its converted
|
|
9200
|
+
* to its underlying type.
|
|
9201
|
+
*/
|
|
9202
|
+
function deEnumerate(t) {
|
|
9203
|
+
if (hasValue(t) && t.type === TypeTag.Enum && t.value.value) {
|
|
9204
|
+
return t.value.value;
|
|
9205
|
+
}
|
|
9206
|
+
if (t.type & TypeTag.Enum) {
|
|
9207
|
+
return {
|
|
9208
|
+
type: (t.type & ~TypeTag.Enum) | TypeTag.Numeric | TypeTag.String,
|
|
9209
|
+
};
|
|
9210
|
+
}
|
|
9211
|
+
return t;
|
|
9212
|
+
}
|
|
9213
|
+
function pushScopedNameType(istate, node, embeddedEffects) {
|
|
9214
|
+
let result;
|
|
9215
|
+
if (istate.typeMap) {
|
|
9216
|
+
result = istate.typeMap?.get(node);
|
|
9217
|
+
}
|
|
9218
|
+
else {
|
|
9219
|
+
const [, results] = istate.state.lookup(node);
|
|
9220
|
+
result =
|
|
9221
|
+
results &&
|
|
9222
|
+
results.reduce((cur, lookupDefn) => lookupDefn.results.reduce((cur, result) => {
|
|
9223
|
+
if (result.type !== "BinaryExpression" &&
|
|
9224
|
+
result.type !== "Identifier") {
|
|
9225
|
+
const type = typeFromTypeStateNode(istate.state, result, true);
|
|
9226
|
+
if (!cur) {
|
|
9227
|
+
cur = cloneType(type);
|
|
9228
|
+
}
|
|
9229
|
+
else {
|
|
9230
|
+
unionInto(cur, type);
|
|
9231
|
+
}
|
|
9232
|
+
}
|
|
9233
|
+
return cur;
|
|
9234
|
+
}, cur), null);
|
|
9235
|
+
}
|
|
9236
|
+
istate.stack.push({
|
|
9237
|
+
value: result || { type: TypeTag.Any },
|
|
9238
|
+
embeddedEffects,
|
|
9239
|
+
node,
|
|
9240
|
+
});
|
|
9241
|
+
}
|
|
9242
|
+
function evaluateNode(istate, node) {
|
|
9243
|
+
const { state, stack } = istate;
|
|
9244
|
+
const push = (item) => {
|
|
9245
|
+
if (!item) {
|
|
9246
|
+
throw new Error("Pushing null");
|
|
9247
|
+
}
|
|
9248
|
+
istate.stack.push(item);
|
|
9249
|
+
};
|
|
9250
|
+
switch (node.type) {
|
|
9251
|
+
case "BinaryExpression": {
|
|
9252
|
+
if (node.operator === "as") {
|
|
9253
|
+
push({
|
|
9254
|
+
value: typeFromTypespec(istate.state, node.right),
|
|
9255
|
+
embeddedEffects: false,
|
|
9256
|
+
node: node.right,
|
|
9257
|
+
});
|
|
9258
|
+
}
|
|
9259
|
+
const right = popIstate(istate, node.right);
|
|
9260
|
+
const left = popIstate(istate, node.left);
|
|
9261
|
+
if (node.operator === "as") {
|
|
9262
|
+
if (hasValue(right.value) && right.value.type === TypeTag.Enum) {
|
|
9263
|
+
if ((left.value.type & (TypeTag.Numeric | TypeTag.String)) ==
|
|
9264
|
+
left.value.type) {
|
|
9265
|
+
right.value.value.value = left.value;
|
|
9266
|
+
stack.push({
|
|
9267
|
+
value: right.value,
|
|
9268
|
+
embeddedEffects: left.embeddedEffects,
|
|
9269
|
+
node,
|
|
9270
|
+
});
|
|
9271
|
+
return;
|
|
9272
|
+
}
|
|
9273
|
+
}
|
|
9274
|
+
push({
|
|
9275
|
+
value: right.value,
|
|
9276
|
+
embeddedEffects: left.embeddedEffects,
|
|
9277
|
+
node,
|
|
9278
|
+
});
|
|
9279
|
+
}
|
|
9280
|
+
else {
|
|
9281
|
+
push({
|
|
9282
|
+
value: evaluateBinaryTypes(node.operator, deEnumerate(left.value), deEnumerate(right.value)),
|
|
9283
|
+
embeddedEffects: left.embeddedEffects || right.embeddedEffects,
|
|
9284
|
+
node,
|
|
9285
|
+
});
|
|
9286
|
+
}
|
|
9287
|
+
break;
|
|
9288
|
+
}
|
|
9289
|
+
case "UnaryExpression":
|
|
9290
|
+
if (node.operator === ":") {
|
|
9291
|
+
push({
|
|
9292
|
+
value: { type: TypeTag.Symbol, value: node.argument.name },
|
|
9293
|
+
embeddedEffects: false,
|
|
9294
|
+
node,
|
|
9295
|
+
});
|
|
9296
|
+
}
|
|
9297
|
+
else if (node.operator !== " as") {
|
|
9298
|
+
const arg = popIstate(istate, node.argument);
|
|
9299
|
+
push({
|
|
9300
|
+
value: evaluateUnaryTypes(node.operator, deEnumerate(arg.value)),
|
|
9301
|
+
embeddedEffects: arg.embeddedEffects,
|
|
9302
|
+
node,
|
|
9303
|
+
});
|
|
9304
|
+
}
|
|
9305
|
+
break;
|
|
9306
|
+
case "SizedArrayExpression": {
|
|
9307
|
+
const arg = popIstate(istate, node.size);
|
|
9308
|
+
push({
|
|
9309
|
+
value: { type: TypeTag.Array },
|
|
9310
|
+
embeddedEffects: arg.embeddedEffects,
|
|
9311
|
+
node,
|
|
9312
|
+
});
|
|
9313
|
+
break;
|
|
9314
|
+
}
|
|
9315
|
+
case "ArrayExpression": {
|
|
9316
|
+
const args = node.elements.length
|
|
9317
|
+
? stack.splice(-node.elements.length)
|
|
9318
|
+
: [];
|
|
9319
|
+
push({
|
|
9320
|
+
value: {
|
|
9321
|
+
type: TypeTag.Array,
|
|
9322
|
+
},
|
|
9323
|
+
embeddedEffects: args.some((arg) => arg.embeddedEffects),
|
|
9324
|
+
node,
|
|
9325
|
+
});
|
|
9326
|
+
break;
|
|
9327
|
+
}
|
|
9328
|
+
case "ObjectExpression": {
|
|
9329
|
+
const args = node.properties.length
|
|
9330
|
+
? stack.splice(-node.properties.length * 2)
|
|
9331
|
+
: [];
|
|
9332
|
+
push({
|
|
9333
|
+
value: {
|
|
9334
|
+
type: TypeTag.Dictionary,
|
|
9335
|
+
},
|
|
9336
|
+
embeddedEffects: args.some((arg) => arg.embeddedEffects),
|
|
9337
|
+
node,
|
|
9338
|
+
});
|
|
9339
|
+
break;
|
|
9340
|
+
}
|
|
9341
|
+
case "ThisExpression": {
|
|
9342
|
+
const self = (() => {
|
|
9343
|
+
for (let i = state.stack.length; i--;) {
|
|
9344
|
+
const si = state.stack[i];
|
|
9345
|
+
if (si.type === "ClassDeclaration") {
|
|
9346
|
+
const klass = { type: TypeTag.Class, value: si };
|
|
9347
|
+
if ((istate.func?.attributes || 0) & StateNodeAttributes.STATIC) {
|
|
9348
|
+
return klass;
|
|
9349
|
+
}
|
|
9350
|
+
else {
|
|
9351
|
+
return { type: TypeTag.Object, value: { klass } };
|
|
9352
|
+
}
|
|
9353
|
+
}
|
|
9354
|
+
if (si.type === "ModuleDeclaration") {
|
|
9355
|
+
return { type: TypeTag.Module, value: si };
|
|
9356
|
+
}
|
|
9357
|
+
}
|
|
9358
|
+
return { type: TypeTag.Module };
|
|
9359
|
+
})();
|
|
9360
|
+
push({ value: self, embeddedEffects: false, node });
|
|
9361
|
+
break;
|
|
9362
|
+
}
|
|
9363
|
+
case "LogicalExpression": {
|
|
9364
|
+
const right = popIstate(istate, node.right);
|
|
9365
|
+
const left = popIstate(istate, node.left);
|
|
9366
|
+
push({
|
|
9367
|
+
value: evaluateLogicalTypes(node.operator, deEnumerate(left.value), deEnumerate(right.value)),
|
|
9368
|
+
embeddedEffects: left.embeddedEffects || right.embeddedEffects,
|
|
9369
|
+
node,
|
|
9370
|
+
});
|
|
9371
|
+
break;
|
|
9372
|
+
}
|
|
9373
|
+
case "ConditionalExpression": {
|
|
9374
|
+
const alternate = popIstate(istate, node.alternate);
|
|
9375
|
+
const consequent = popIstate(istate, node.consequent);
|
|
9376
|
+
const test = popIstate(istate, node.test);
|
|
9377
|
+
const testType = deEnumerate(test.value);
|
|
9378
|
+
if (mustBeTrue(testType)) {
|
|
9379
|
+
push({
|
|
9380
|
+
value: consequent.value,
|
|
9381
|
+
embeddedEffects: test.embeddedEffects || consequent.embeddedEffects,
|
|
9382
|
+
node,
|
|
9383
|
+
});
|
|
9384
|
+
}
|
|
9385
|
+
else if (mustBeFalse(testType)) {
|
|
9386
|
+
push({
|
|
9387
|
+
value: alternate.value,
|
|
9388
|
+
embeddedEffects: test.embeddedEffects || alternate.embeddedEffects,
|
|
9389
|
+
node,
|
|
9390
|
+
});
|
|
9391
|
+
}
|
|
9392
|
+
else {
|
|
9393
|
+
const value = cloneType(consequent.value);
|
|
9394
|
+
unionInto(value, alternate.value);
|
|
9395
|
+
push({
|
|
9396
|
+
value,
|
|
9397
|
+
embeddedEffects: test.embeddedEffects ||
|
|
9398
|
+
alternate.embeddedEffects ||
|
|
9399
|
+
consequent.embeddedEffects,
|
|
9400
|
+
node,
|
|
9401
|
+
});
|
|
9402
|
+
}
|
|
9403
|
+
break;
|
|
9404
|
+
}
|
|
9405
|
+
case "ParenthesizedExpression": {
|
|
9406
|
+
const { value, embeddedEffects } = popIstate(istate, node.expression);
|
|
9407
|
+
push({ value, embeddedEffects, node });
|
|
9408
|
+
break;
|
|
9409
|
+
}
|
|
9410
|
+
case "Literal":
|
|
9411
|
+
push({
|
|
9412
|
+
value: typeFromLiteral(node),
|
|
9413
|
+
embeddedEffects: false,
|
|
9414
|
+
node,
|
|
9415
|
+
});
|
|
9416
|
+
break;
|
|
9417
|
+
case "Identifier":
|
|
9418
|
+
pushScopedNameType(istate, node, false);
|
|
9419
|
+
break;
|
|
9420
|
+
case "MemberExpression":
|
|
9421
|
+
if (!(0,external_api_cjs_namespaceObject.isLookupCandidate)(node)) {
|
|
9422
|
+
const property = popIstate(istate, node.property);
|
|
9423
|
+
const object = popIstate(istate, node.object);
|
|
9424
|
+
if (hasValue(object.value) &&
|
|
9425
|
+
object.value.type === TypeTag.Array &&
|
|
9426
|
+
property.value.type & (TypeTag.Number | TypeTag.Long)) {
|
|
9427
|
+
push({
|
|
9428
|
+
value: object.value.value,
|
|
9429
|
+
embeddedEffects: object.embeddedEffects || property.embeddedEffects,
|
|
9430
|
+
node,
|
|
9431
|
+
});
|
|
9432
|
+
break;
|
|
9433
|
+
}
|
|
9434
|
+
if (hasValue(object.value) &&
|
|
9435
|
+
object.value.type === TypeTag.Dictionary &&
|
|
9436
|
+
property.value.type & object.value.value.key.type) {
|
|
9437
|
+
const value = { type: TypeTag.Null };
|
|
9438
|
+
unionInto(value, object.value.value.value);
|
|
9439
|
+
push({
|
|
9440
|
+
value,
|
|
9441
|
+
embeddedEffects: object.embeddedEffects || property.embeddedEffects,
|
|
9442
|
+
node,
|
|
9443
|
+
});
|
|
9444
|
+
break;
|
|
9445
|
+
}
|
|
9446
|
+
push({
|
|
9447
|
+
value: { type: TypeTag.Any },
|
|
9448
|
+
embeddedEffects: object.embeddedEffects || property.embeddedEffects,
|
|
9449
|
+
node,
|
|
9450
|
+
});
|
|
9451
|
+
}
|
|
9452
|
+
else {
|
|
9453
|
+
const object = popIstate(istate, node.object);
|
|
9454
|
+
pushScopedNameType(istate, node, object.embeddedEffects);
|
|
9455
|
+
}
|
|
9456
|
+
break;
|
|
9457
|
+
case "SequenceExpression": {
|
|
9458
|
+
if (stack.length < node.expressions.length) {
|
|
9459
|
+
throw new Error("Unbalanced stack");
|
|
9460
|
+
}
|
|
9461
|
+
if (node.expressions.length) {
|
|
9462
|
+
const right = popIstate(istate, node.expressions[node.expressions.length - 1]);
|
|
9463
|
+
if (node.expressions.length > 1) {
|
|
9464
|
+
right.embeddedEffects =
|
|
9465
|
+
stack
|
|
9466
|
+
.splice(1 - node.expressions.length)
|
|
9467
|
+
.some(({ embeddedEffects }) => embeddedEffects) ||
|
|
9468
|
+
right.embeddedEffects;
|
|
9469
|
+
}
|
|
9470
|
+
push({
|
|
9471
|
+
value: right.value,
|
|
9472
|
+
embeddedEffects: right.embeddedEffects,
|
|
9473
|
+
node,
|
|
9474
|
+
});
|
|
9475
|
+
}
|
|
9476
|
+
break;
|
|
9477
|
+
}
|
|
9478
|
+
case "AssignmentExpression": {
|
|
9479
|
+
const right = popIstate(istate, node.right);
|
|
9480
|
+
const left = popIstate(istate, node.left);
|
|
9481
|
+
if (node.operator === "=") {
|
|
9482
|
+
push({
|
|
9483
|
+
value: right.value,
|
|
9484
|
+
embeddedEffects: true,
|
|
9485
|
+
node,
|
|
9486
|
+
});
|
|
9487
|
+
}
|
|
9488
|
+
else {
|
|
9489
|
+
push({
|
|
9490
|
+
value: evaluateBinaryTypes(node.operator.slice(0, -1), left.value, right.value),
|
|
9491
|
+
embeddedEffects: true,
|
|
9492
|
+
node,
|
|
9493
|
+
});
|
|
9494
|
+
}
|
|
9495
|
+
break;
|
|
9496
|
+
}
|
|
9497
|
+
case "UpdateExpression": {
|
|
9498
|
+
const right = { type: TypeTag.Number, value: 1 };
|
|
9499
|
+
const left = popIstate(istate, node.argument);
|
|
9500
|
+
push({
|
|
9501
|
+
value: evaluateBinaryTypes(node.operator.slice(1), left.value, right),
|
|
9502
|
+
embeddedEffects: true,
|
|
9503
|
+
node,
|
|
9504
|
+
});
|
|
9505
|
+
break;
|
|
9506
|
+
}
|
|
9507
|
+
case "NewExpression": {
|
|
9508
|
+
const [klass, ..._args] = stack.splice(-1 - node.arguments.length);
|
|
9509
|
+
// we should check the arguments at some point...
|
|
9510
|
+
const obj = { type: TypeTag.Object };
|
|
9511
|
+
if (isExact(klass.value) && klass.value.type === TypeTag.Class) {
|
|
9512
|
+
obj.value = { klass: klass.value };
|
|
9513
|
+
}
|
|
9514
|
+
push({ value: obj, embeddedEffects: true, node });
|
|
9515
|
+
break;
|
|
9516
|
+
}
|
|
9517
|
+
case "CallExpression": {
|
|
9518
|
+
const [callee, ...args] = stack.splice(-1 - node.arguments.length);
|
|
9519
|
+
push({
|
|
9520
|
+
value: evaluateCall(state, node, callee.value, args.map(({ value }) => value)),
|
|
9521
|
+
embeddedEffects: true,
|
|
9522
|
+
node,
|
|
9523
|
+
});
|
|
9524
|
+
break;
|
|
9525
|
+
}
|
|
9526
|
+
// Statements, and other
|
|
9527
|
+
case "VariableDeclarator":
|
|
9528
|
+
case "EnumStringMember":
|
|
9529
|
+
if (node.init)
|
|
9530
|
+
popIstate(istate, node.init);
|
|
9531
|
+
break;
|
|
9532
|
+
case "ExpressionStatement":
|
|
9533
|
+
popIstate(istate, node.expression);
|
|
9534
|
+
break;
|
|
9535
|
+
case "ReturnStatement":
|
|
9536
|
+
if (node.argument) {
|
|
9537
|
+
popIstate(istate, node.argument);
|
|
9538
|
+
}
|
|
9539
|
+
break;
|
|
9540
|
+
case "IfStatement":
|
|
9541
|
+
case "WhileStatement":
|
|
9542
|
+
case "DoWhileStatement":
|
|
9543
|
+
popIstate(istate, node.test);
|
|
9544
|
+
break;
|
|
9545
|
+
case "SwitchStatement":
|
|
9546
|
+
popIstate(istate, node.discriminant);
|
|
9547
|
+
break;
|
|
9548
|
+
case "SwitchCase":
|
|
9549
|
+
if (node.test)
|
|
9550
|
+
popIstate(istate, node.test);
|
|
9551
|
+
break;
|
|
9552
|
+
case "InstanceOfCase": {
|
|
9553
|
+
const klass = popIstate(istate, node.id);
|
|
9554
|
+
push({
|
|
9555
|
+
value: { type: TypeTag.Boolean },
|
|
9556
|
+
embeddedEffects: klass.embeddedEffects,
|
|
9557
|
+
node,
|
|
9558
|
+
});
|
|
9559
|
+
break;
|
|
9560
|
+
}
|
|
9561
|
+
case "BlockStatement":
|
|
9562
|
+
case "BreakStatement":
|
|
9563
|
+
case "ContinueStatement":
|
|
9564
|
+
case "TryStatement":
|
|
9565
|
+
break;
|
|
9566
|
+
case "ThrowStatement":
|
|
9567
|
+
popIstate(istate, node.argument);
|
|
9568
|
+
break;
|
|
9569
|
+
case "ForStatement":
|
|
9570
|
+
if (node.update)
|
|
9571
|
+
popIstate(istate, node.update);
|
|
9572
|
+
if (node.test)
|
|
9573
|
+
popIstate(istate, node.test);
|
|
9574
|
+
if (node.init && node.init.type !== "VariableDeclaration") {
|
|
9575
|
+
popIstate(istate, node.init);
|
|
9576
|
+
}
|
|
9577
|
+
break;
|
|
9578
|
+
case "ImportModule":
|
|
9579
|
+
popIstate(istate, node.id);
|
|
9580
|
+
break;
|
|
9581
|
+
case "Using":
|
|
9582
|
+
if (node.as)
|
|
9583
|
+
popIstate(istate, node.as);
|
|
9584
|
+
popIstate(istate, node.id);
|
|
9585
|
+
break;
|
|
9586
|
+
case "ClassDeclaration":
|
|
9587
|
+
case "EnumDeclaration":
|
|
9588
|
+
case "FunctionDeclaration":
|
|
9589
|
+
case "ModuleDeclaration":
|
|
9590
|
+
case "TypedefDeclaration":
|
|
9591
|
+
case "VariableDeclaration":
|
|
9592
|
+
case "Program":
|
|
9593
|
+
case "TypeSpecList":
|
|
9594
|
+
case "CatchClause":
|
|
9595
|
+
case "CatchClauses":
|
|
9596
|
+
case "EnumStringBody":
|
|
9597
|
+
case "Property":
|
|
9598
|
+
case "AttributeList":
|
|
9599
|
+
case "Attributes":
|
|
9600
|
+
case "TypeSpecPart":
|
|
9601
|
+
case "ClassElement":
|
|
9602
|
+
case "ClassBody":
|
|
9603
|
+
case "MethodDefinition":
|
|
9604
|
+
case "Block":
|
|
9605
|
+
case "Line":
|
|
9606
|
+
case "MultiLine":
|
|
9607
|
+
break;
|
|
9608
|
+
default:
|
|
9609
|
+
unhandledType(node);
|
|
9610
|
+
}
|
|
9611
|
+
}
|
|
9612
|
+
function roundToFloat(value) {
|
|
9613
|
+
return new Float32Array([value])[0];
|
|
9614
|
+
}
|
|
9615
|
+
|
|
9616
|
+
;// CONCATENATED MODULE: ./src/type-flow.ts
|
|
9617
|
+
|
|
9618
|
+
|
|
9619
|
+
|
|
9620
|
+
|
|
9621
|
+
|
|
9622
|
+
|
|
9623
|
+
|
|
9624
|
+
|
|
9625
|
+
const type_flow_logging = true;
|
|
9626
|
+
function type_flow_buildTypeInfo(state, func) {
|
|
9627
|
+
if (!func.node.body || !func.stack)
|
|
9628
|
+
return;
|
|
9629
|
+
const { graph } = buildDataFlowGraph(state, func, () => false, false, true);
|
|
9630
|
+
state = { ...state, stack: func.stack };
|
|
9631
|
+
return propagateTypes(state, func, graph);
|
|
9632
|
+
}
|
|
9633
|
+
function mergeTypeState(to, from) {
|
|
9634
|
+
let changes = false;
|
|
9635
|
+
from.forEach((v, k) => {
|
|
9636
|
+
const tov = to.get(k);
|
|
9637
|
+
let result;
|
|
9638
|
+
if (tov) {
|
|
9639
|
+
if (!unionInto((result = cloneType(tov)), v))
|
|
9640
|
+
return;
|
|
9641
|
+
}
|
|
9642
|
+
else {
|
|
9643
|
+
result = v;
|
|
9644
|
+
}
|
|
9645
|
+
to.set(k, result);
|
|
9646
|
+
changes = true;
|
|
9647
|
+
});
|
|
9648
|
+
return changes;
|
|
9649
|
+
}
|
|
9650
|
+
function printBlockHeader(block) {
|
|
9651
|
+
console.log(block.order, `(${block.node?.loc?.source || "??"}:${block.node?.loc?.start.line || "??"})`, `Preds: ${(block.preds || [])
|
|
9652
|
+
.map((block) => block.order)
|
|
9653
|
+
.join(", ")}`);
|
|
9654
|
+
}
|
|
9655
|
+
function printBlockEvents(block, typeMap) {
|
|
9656
|
+
(0,external_util_cjs_namespaceObject.forEach)(block.events, (event) => event.type !== "exn" &&
|
|
9657
|
+
console.log(` ${event.type}: ${event.decl ? declFullName(event.decl) : "??"} ${event.type === "ref" && typeMap && typeMap.has(event.node)
|
|
9658
|
+
? display(typeMap.get(event.node))
|
|
9659
|
+
: ""}`));
|
|
9660
|
+
}
|
|
9661
|
+
function printBlockTrailer(block) {
|
|
9662
|
+
console.log(`Succs: ${(block.succs || [])
|
|
9663
|
+
.map((block) => block.order)
|
|
9664
|
+
.join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
|
|
9665
|
+
}
|
|
9666
|
+
function printBlockState(block, state) {
|
|
9667
|
+
state.forEach((value, key) => {
|
|
9668
|
+
console.log(`${(0,external_util_cjs_namespaceObject.map)(key, (k) => {
|
|
9669
|
+
if (k.type === "Literal") {
|
|
9670
|
+
return k.raw;
|
|
9671
|
+
}
|
|
9672
|
+
else if ((0,external_api_cjs_namespaceObject.isStateNode)(k)) {
|
|
9673
|
+
return k.fullName;
|
|
9674
|
+
}
|
|
9675
|
+
else if (k.type === "BinaryExpression") {
|
|
9676
|
+
return k.left.name;
|
|
9677
|
+
}
|
|
9678
|
+
else if (k.type === "Identifier") {
|
|
9679
|
+
return k.name;
|
|
9680
|
+
}
|
|
9681
|
+
else if (k.type === "EnumStringMember") {
|
|
9682
|
+
return k.id.name;
|
|
9683
|
+
}
|
|
9684
|
+
return "<unknown>";
|
|
9685
|
+
}).join("|")} = ${display(value)}`);
|
|
9686
|
+
});
|
|
9687
|
+
}
|
|
9688
|
+
function propagateTypes(state, func, graph) {
|
|
9689
|
+
// We want to traverse the blocks in reverse post order, in
|
|
9690
|
+
// order to propagate the "availability" of the types.
|
|
9691
|
+
const order = getPostOrder(graph).reverse();
|
|
9692
|
+
const queue = new DataflowQueue();
|
|
9693
|
+
const blockStates = order.map((block, i) => {
|
|
9694
|
+
block.order = i;
|
|
9695
|
+
queue.enqueue(block);
|
|
9696
|
+
return new Map();
|
|
9697
|
+
});
|
|
9698
|
+
if (type_flow_logging && process.env["TYPEFLOW_FUNC"] === func.fullName) {
|
|
9699
|
+
order.forEach((block) => {
|
|
9700
|
+
printBlockHeader(block);
|
|
9701
|
+
printBlockEvents(block);
|
|
9702
|
+
printBlockTrailer(block);
|
|
9703
|
+
});
|
|
9704
|
+
}
|
|
9705
|
+
const typeMap = new Map();
|
|
9706
|
+
const istate = {
|
|
9707
|
+
state,
|
|
9708
|
+
typeMap,
|
|
9709
|
+
stack: [],
|
|
9710
|
+
};
|
|
9711
|
+
const modifiableDecl = (decls, callees) => (0,external_util_cjs_namespaceObject.some)(decls, (decl) => decl.type === "VariableDeclarator" &&
|
|
9712
|
+
decl.node.kind === "var" &&
|
|
9713
|
+
!(0,external_api_cjs_namespaceObject.isLocal)(decl) &&
|
|
9714
|
+
(!callees ||
|
|
9715
|
+
callees.some((callee) => functionMayModify(state, callee, decl))));
|
|
9716
|
+
const declInitVal = (decls) => {
|
|
9717
|
+
return (0,external_util_cjs_namespaceObject.reduce)(decls, (result, decl) => {
|
|
9718
|
+
if (decl.type === "Identifier" || decl.type === "BinaryExpression") {
|
|
9719
|
+
// It looks like this can happen due to catch clauses
|
|
9720
|
+
// throw new Error(`Internal error: Unexpected function parameter`);
|
|
9721
|
+
unionInto(result, { type: TypeTag.Any });
|
|
9722
|
+
return result;
|
|
9723
|
+
}
|
|
9724
|
+
const declType = decl.type === "Literal"
|
|
9725
|
+
? typeFromLiteral(decl)
|
|
9726
|
+
: typeFromTypeStateNode(state, decl, true);
|
|
9727
|
+
unionInto(result, declType);
|
|
9728
|
+
return result;
|
|
9729
|
+
}, { type: TypeTag.Never });
|
|
9730
|
+
};
|
|
9731
|
+
const head = blockStates[0];
|
|
9732
|
+
// set the parameters to their initial types
|
|
9733
|
+
func.node.params.forEach((param) => {
|
|
9734
|
+
head.set(param, param.type === "BinaryExpression"
|
|
9735
|
+
? typeFromTypespec(state, param.right)
|
|
9736
|
+
: { type: TypeTag.Any });
|
|
9737
|
+
});
|
|
9738
|
+
// set every other modifiable (ie non-local variables that
|
|
9739
|
+
// could be affected by "mod" events) decl to its initial
|
|
9740
|
+
// value too.
|
|
9741
|
+
order.forEach((block) => {
|
|
9742
|
+
(0,external_util_cjs_namespaceObject.forEach)(block.events, (event) => {
|
|
9743
|
+
if (event.type === "ref" &&
|
|
9744
|
+
!head.has(event.decl) &&
|
|
9745
|
+
modifiableDecl(event.decl)) {
|
|
9746
|
+
head.set(event.decl, declInitVal(event.decl));
|
|
9747
|
+
}
|
|
9748
|
+
});
|
|
9749
|
+
});
|
|
9750
|
+
while (!queue.empty()) {
|
|
9751
|
+
const top = queue.dequeue();
|
|
9752
|
+
if (top.order === undefined) {
|
|
9753
|
+
throw new Error(`Unreachable block was visited!`);
|
|
9754
|
+
}
|
|
9755
|
+
const curState = new Map(blockStates[top.order]);
|
|
9756
|
+
if (top.events) {
|
|
9757
|
+
for (let i = 0; i < top.events.length; i++) {
|
|
9758
|
+
const event = top.events[i];
|
|
9759
|
+
if (event.mayThrow && top.exsucc) {
|
|
9760
|
+
const succState = blockStates[top.exsucc.order];
|
|
9761
|
+
if (succState) {
|
|
9762
|
+
if (mergeTypeState(succState, curState)) {
|
|
9763
|
+
queue.enqueue(top.exsucc);
|
|
9764
|
+
}
|
|
9765
|
+
}
|
|
9766
|
+
}
|
|
9767
|
+
switch (event.type) {
|
|
9768
|
+
case "ref": {
|
|
9769
|
+
let curType = curState.get(event.decl);
|
|
9770
|
+
if (!curType) {
|
|
9771
|
+
curState.set(event.decl, (curType = declInitVal(event.decl)));
|
|
9772
|
+
}
|
|
9773
|
+
typeMap.set(event.node, curType);
|
|
9774
|
+
break;
|
|
9775
|
+
}
|
|
9776
|
+
case "mod": {
|
|
9777
|
+
curState.forEach((_type, decls) => {
|
|
9778
|
+
if (modifiableDecl(decls, event.callees)) {
|
|
9779
|
+
curState.set(decls, declInitVal(decls));
|
|
9780
|
+
}
|
|
9781
|
+
});
|
|
9782
|
+
if (event.node.type === "CallExpression") {
|
|
9783
|
+
typeMap.set(event.node, interp_evaluate(istate, event.node).value);
|
|
9784
|
+
}
|
|
9785
|
+
break;
|
|
9786
|
+
}
|
|
9787
|
+
case "def": {
|
|
9788
|
+
const beforeType = curState.get(event.decl);
|
|
9789
|
+
const lval = event.node.type === "UpdateExpression"
|
|
9790
|
+
? event.node.argument
|
|
9791
|
+
: event.node.type === "AssignmentExpression"
|
|
9792
|
+
? event.node.left
|
|
9793
|
+
: null;
|
|
9794
|
+
if (beforeType && lval) {
|
|
9795
|
+
typeMap.set(lval, beforeType);
|
|
9796
|
+
}
|
|
9797
|
+
const expr = event.node.type === "VariableDeclarator"
|
|
9798
|
+
? event.node.init || null
|
|
9799
|
+
: event.node;
|
|
9800
|
+
if (expr) {
|
|
9801
|
+
const type = interp_evaluate(istate, expr);
|
|
9802
|
+
curState.set(event.decl, type.value);
|
|
9803
|
+
}
|
|
9804
|
+
else {
|
|
9805
|
+
curState.set(event.decl, { type: TypeTag.Any });
|
|
9806
|
+
}
|
|
9807
|
+
break;
|
|
9808
|
+
}
|
|
9809
|
+
}
|
|
9810
|
+
}
|
|
9811
|
+
}
|
|
9812
|
+
top.succs?.forEach((succ) => {
|
|
9813
|
+
if (succ.order == null) {
|
|
9814
|
+
throw new Error("Unreachable block was visited");
|
|
9815
|
+
}
|
|
9816
|
+
if (mergeTypeState(blockStates[succ.order], curState)) {
|
|
9817
|
+
queue.enqueue(succ);
|
|
9818
|
+
}
|
|
9819
|
+
});
|
|
9820
|
+
}
|
|
9821
|
+
if (type_flow_logging && process.env["TYPEFLOW_FUNC"] === func.fullName) {
|
|
9822
|
+
order.forEach((block) => {
|
|
9823
|
+
printBlockHeader(block);
|
|
9824
|
+
printBlockState(block, blockStates[block.order]);
|
|
9825
|
+
printBlockEvents(block, typeMap);
|
|
9826
|
+
printBlockTrailer(block);
|
|
9827
|
+
});
|
|
9828
|
+
console.log("====== TypeMap =====");
|
|
9829
|
+
typeMap.forEach((value, key) => {
|
|
9830
|
+
console.log(`${(0,external_api_cjs_namespaceObject.formatAst)(key)} = ${display(value)}`);
|
|
9831
|
+
});
|
|
9832
|
+
}
|
|
9833
|
+
return istate;
|
|
9834
|
+
}
|
|
9835
|
+
|
|
9836
|
+
;// CONCATENATED MODULE: ./src/type-flow/could-be.ts
|
|
9837
|
+
|
|
9838
|
+
|
|
9839
|
+
|
|
9840
|
+
|
|
9841
|
+
/*
|
|
9842
|
+
* Determine whether a value conforming to a's type could also
|
|
9843
|
+
* be a value conforming to b's type.
|
|
9844
|
+
*
|
|
9845
|
+
* This is symmetric, and a subtypeOf b, or b subtypeOf a implies
|
|
9846
|
+
* a couldBe b.
|
|
9847
|
+
*/
|
|
9848
|
+
function couldBe(a, b) {
|
|
9849
|
+
const common = a.type & b.type & ~TypeTag.Typedef;
|
|
9850
|
+
if (common) {
|
|
9851
|
+
if (a.value == null)
|
|
9852
|
+
return true;
|
|
9853
|
+
if (b.value == null)
|
|
9854
|
+
return true;
|
|
9855
|
+
let result = false;
|
|
9856
|
+
forEachUnionComponent(a, common, (bit, avalue) => {
|
|
9857
|
+
if (avalue == null) {
|
|
9858
|
+
result = true;
|
|
9859
|
+
return false;
|
|
9860
|
+
}
|
|
9861
|
+
const bvalue = getUnionComponent(b, bit);
|
|
9862
|
+
if (bvalue == null || couldBeValue(bit, avalue, bvalue)) {
|
|
9863
|
+
result = true;
|
|
9864
|
+
return false;
|
|
9865
|
+
}
|
|
9866
|
+
return true;
|
|
9867
|
+
});
|
|
9868
|
+
if (result)
|
|
9869
|
+
return true;
|
|
9870
|
+
}
|
|
9871
|
+
if ((a.type & TypeTag.Enum && b.type & (TypeTag.Numeric | TypeTag.String)) ||
|
|
9872
|
+
(b.type & TypeTag.Enum && a.type & (TypeTag.Numeric | TypeTag.String))) {
|
|
9873
|
+
return true;
|
|
9874
|
+
}
|
|
9875
|
+
if (a.type & TypeTag.Object &&
|
|
9876
|
+
b.type & ObjectLikeTagsConst &&
|
|
9877
|
+
getObjectValue(a) == null) {
|
|
9878
|
+
/*
|
|
9879
|
+
* converting Number, String etc to and from Object is ok,
|
|
9880
|
+
* because they *are* subtypes of Object. But converting
|
|
9881
|
+
* them from `Object<something>` is not, because `something`
|
|
9882
|
+
* will never be Number, String, etc.
|
|
9883
|
+
*
|
|
9884
|
+
* So we only add Object when the other side has an unqualified
|
|
9885
|
+
* object type.
|
|
9886
|
+
*/
|
|
9887
|
+
return true;
|
|
9888
|
+
}
|
|
9889
|
+
if (b.type & TypeTag.Object &&
|
|
9890
|
+
a.type & ObjectLikeTagsConst &&
|
|
9891
|
+
getObjectValue(b) == null) {
|
|
9892
|
+
return true;
|
|
9893
|
+
}
|
|
9894
|
+
const checkTypdef = (t, other) => {
|
|
9895
|
+
const typedef = getUnionComponent(t, TypeTag.Typedef);
|
|
9896
|
+
return (typedef &&
|
|
9897
|
+
(0,external_util_cjs_namespaceObject.some)(typedef, (td) => {
|
|
9898
|
+
if (!td.resolvedType) {
|
|
9899
|
+
throw new Error(`No resolved type for ${td.fullName} in 'couldBe'`);
|
|
9900
|
+
}
|
|
9901
|
+
return couldBe(td.resolvedType, other);
|
|
9902
|
+
}));
|
|
9903
|
+
};
|
|
9904
|
+
if (a.type & TypeTag.Typedef && checkTypdef(a, b)) {
|
|
9905
|
+
return true;
|
|
9906
|
+
}
|
|
9907
|
+
if (b.type & TypeTag.Typedef && checkTypdef(b, a)) {
|
|
9908
|
+
return true;
|
|
9909
|
+
}
|
|
9910
|
+
return false;
|
|
9911
|
+
}
|
|
9912
|
+
function couldBeValue(bit, avalue, bvalue) {
|
|
9913
|
+
switch (bit) {
|
|
9914
|
+
case TypeTag.Null:
|
|
9915
|
+
case TypeTag.False:
|
|
9916
|
+
case TypeTag.True:
|
|
9917
|
+
case TypeTag.Typedef:
|
|
9918
|
+
throw new Error(`Unexpected TypeTag '${TypeTag[bit]}'`);
|
|
9919
|
+
case TypeTag.Number:
|
|
9920
|
+
case TypeTag.Long:
|
|
9921
|
+
case TypeTag.Float:
|
|
9922
|
+
case TypeTag.Double:
|
|
9923
|
+
case TypeTag.String:
|
|
9924
|
+
case TypeTag.Char:
|
|
9925
|
+
case TypeTag.Symbol:
|
|
9926
|
+
return avalue === bvalue;
|
|
9927
|
+
case TypeTag.Array:
|
|
9928
|
+
return couldBe(avalue, bvalue);
|
|
9929
|
+
case TypeTag.Dictionary: {
|
|
9930
|
+
const adict = avalue;
|
|
9931
|
+
const bdict = bvalue;
|
|
9932
|
+
return couldBe(adict.key, bdict.key) && couldBe(adict.value, bdict.value);
|
|
9933
|
+
}
|
|
9934
|
+
case TypeTag.Module:
|
|
9935
|
+
case TypeTag.Function: {
|
|
9936
|
+
const asd = avalue;
|
|
9937
|
+
const bsd = bvalue;
|
|
9938
|
+
// quadratic :-(
|
|
9939
|
+
return (0,external_util_cjs_namespaceObject.some)(asd, (sna) => (0,external_util_cjs_namespaceObject.some)(bsd, (snb) => sna === snb));
|
|
9940
|
+
}
|
|
9941
|
+
case TypeTag.Class: {
|
|
9942
|
+
const asd = avalue;
|
|
9943
|
+
const bsd = bvalue;
|
|
9944
|
+
return (0,external_util_cjs_namespaceObject.some)(asd, (sna) => {
|
|
9945
|
+
const superA = (0,external_api_cjs_namespaceObject.getSuperClasses)(sna);
|
|
9946
|
+
return (0,external_util_cjs_namespaceObject.some)(bsd, (snb) => {
|
|
9947
|
+
if (sna === snb || (superA && superA.has(snb))) {
|
|
9948
|
+
return true;
|
|
9949
|
+
}
|
|
9950
|
+
const superB = (0,external_api_cjs_namespaceObject.getSuperClasses)(snb);
|
|
9951
|
+
return superB && superB.has(sna);
|
|
9952
|
+
});
|
|
9953
|
+
});
|
|
9954
|
+
}
|
|
9955
|
+
case TypeTag.Object: {
|
|
9956
|
+
const aobj = avalue;
|
|
9957
|
+
const bobj = bvalue;
|
|
9958
|
+
return couldBe(aobj.klass, bobj.klass) && couldBeObj(aobj.obj, bobj.obj);
|
|
9959
|
+
}
|
|
9960
|
+
case TypeTag.Enum: {
|
|
9961
|
+
const aenum = avalue;
|
|
9962
|
+
const benum = bvalue;
|
|
9963
|
+
return (aenum.enum === benum.enum &&
|
|
9964
|
+
(!aenum.value || !benum.value || couldBe(aenum.value, benum.value)));
|
|
9965
|
+
}
|
|
9966
|
+
default:
|
|
9967
|
+
unhandledType(bit);
|
|
9968
|
+
}
|
|
9969
|
+
}
|
|
9970
|
+
function couldBeObj(a, b) {
|
|
9971
|
+
if (!a || !b)
|
|
9972
|
+
return true;
|
|
9973
|
+
return Object.entries(a).every(([key, value]) => {
|
|
9974
|
+
if (!(0,external_api_cjs_namespaceObject.hasProperty)(b, key))
|
|
9975
|
+
return true;
|
|
9976
|
+
return couldBe(value, b[key]);
|
|
9977
|
+
});
|
|
9978
|
+
}
|
|
9979
|
+
|
|
9980
|
+
;// CONCATENATED MODULE: ./src/type-flow/optimize.ts
|
|
9981
|
+
|
|
9982
|
+
|
|
9983
|
+
|
|
9984
|
+
|
|
9985
|
+
|
|
9986
|
+
|
|
9987
|
+
|
|
9988
|
+
|
|
9989
|
+
function optimizeFunction(state, func) {
|
|
9990
|
+
const istate = buildTypeInfo(state, func);
|
|
9991
|
+
if (!istate)
|
|
9992
|
+
return;
|
|
9993
|
+
evaluate({
|
|
9994
|
+
...istate,
|
|
9995
|
+
pre(node) {
|
|
9996
|
+
return beforeEvaluate(this, node);
|
|
9997
|
+
},
|
|
9998
|
+
post(node) {
|
|
9999
|
+
return afterEvaluate(this, node);
|
|
10000
|
+
},
|
|
10001
|
+
}, func.node.body);
|
|
10002
|
+
}
|
|
10003
|
+
function beforeEvaluate(istate, node) {
|
|
10004
|
+
switch (node.type) {
|
|
10005
|
+
case "ConditionalExpression": {
|
|
10006
|
+
let alternate = popIstate(istate, node.alternate);
|
|
10007
|
+
let consequent = popIstate(istate, node.consequent);
|
|
10008
|
+
const test = popIstate(istate, node.test);
|
|
10009
|
+
const result = mustBeTrue(test.value)
|
|
10010
|
+
? true
|
|
10011
|
+
: mustBeFalse(test.value)
|
|
10012
|
+
? false
|
|
10013
|
+
: null;
|
|
10014
|
+
if (result !== null) {
|
|
10015
|
+
if (!test.embeddedEffects) {
|
|
10016
|
+
const arg = result ? consequent : alternate;
|
|
10017
|
+
istate.stack.push(arg);
|
|
10018
|
+
return result ? node.consequent : node.alternate;
|
|
10019
|
+
}
|
|
10020
|
+
}
|
|
10021
|
+
if (node.alternate &&
|
|
10022
|
+
node.test.type === "UnaryExpression" &&
|
|
10023
|
+
node.test.operator === "!" &&
|
|
10024
|
+
test.value.type === TypeTag.Boolean) {
|
|
10025
|
+
const alternateNode = node.alternate;
|
|
10026
|
+
node.alternate = node.consequent;
|
|
10027
|
+
node.consequent = alternateNode;
|
|
10028
|
+
const tmp = alternate;
|
|
10029
|
+
alternate = consequent;
|
|
10030
|
+
consequent = tmp;
|
|
10031
|
+
test.node = node.test = node.test.argument;
|
|
10032
|
+
}
|
|
10033
|
+
if (test.value.type === TypeTag.Boolean &&
|
|
10034
|
+
((consequent.value.type === TypeTag.True &&
|
|
10035
|
+
alternate.value.type === TypeTag.False) ||
|
|
10036
|
+
(consequent.value.type === TypeTag.False &&
|
|
10037
|
+
alternate.value.type === TypeTag.True)) &&
|
|
10038
|
+
!consequent.embeddedEffects &&
|
|
10039
|
+
!alternate.embeddedEffects) {
|
|
10040
|
+
if (consequent.value.type === TypeTag.False) {
|
|
10041
|
+
test.node = wrap({
|
|
10042
|
+
type: "UnaryExpression",
|
|
10043
|
+
operator: "!",
|
|
10044
|
+
argument: node.test,
|
|
10045
|
+
prefix: true,
|
|
10046
|
+
}, test.node.loc);
|
|
10047
|
+
}
|
|
10048
|
+
istate.stack.push(test);
|
|
10049
|
+
return test.node;
|
|
10050
|
+
}
|
|
10051
|
+
istate.stack.push(test, consequent, alternate);
|
|
10052
|
+
break;
|
|
10053
|
+
}
|
|
10054
|
+
case "IfStatement": {
|
|
10055
|
+
const test = popIstate(istate, node.test);
|
|
10056
|
+
const result = mustBeTrue(test.value)
|
|
10057
|
+
? true
|
|
10058
|
+
: mustBeFalse(test.value)
|
|
10059
|
+
? false
|
|
10060
|
+
: null;
|
|
10061
|
+
if (result !== null) {
|
|
10062
|
+
const rep = result ? node.consequent : node.alternate || false;
|
|
10063
|
+
if (!test.embeddedEffects) {
|
|
10064
|
+
return rep;
|
|
10065
|
+
}
|
|
10066
|
+
const estmt = wrap({ type: "ExpressionStatement", expression: node.test }, node.loc);
|
|
10067
|
+
if (!rep) {
|
|
10068
|
+
return estmt;
|
|
10069
|
+
}
|
|
10070
|
+
if (rep.type === "BlockStatement") {
|
|
10071
|
+
rep.body.unshift(estmt);
|
|
10072
|
+
return rep;
|
|
10073
|
+
}
|
|
10074
|
+
}
|
|
10075
|
+
if (node.alternate &&
|
|
10076
|
+
node.test.type === "UnaryExpression" &&
|
|
10077
|
+
node.test.operator === "!" &&
|
|
10078
|
+
test.value.type === TypeTag.Boolean) {
|
|
10079
|
+
const alternate = node.alternate;
|
|
10080
|
+
node.alternate = node.consequent;
|
|
10081
|
+
node.consequent = alternate;
|
|
10082
|
+
test.node = node.test = node.test.argument;
|
|
10083
|
+
}
|
|
10084
|
+
istate.stack.push(test);
|
|
10085
|
+
break;
|
|
10086
|
+
}
|
|
10087
|
+
case "WhileStatement":
|
|
10088
|
+
case "DoWhileStatement": {
|
|
10089
|
+
const test = popIstate(istate, node.test);
|
|
10090
|
+
if (!test.embeddedEffects) {
|
|
10091
|
+
if (mustBeFalse(test.value)) {
|
|
10092
|
+
return node.type === "WhileStatement" ? false : node.body;
|
|
10093
|
+
}
|
|
10094
|
+
}
|
|
10095
|
+
istate.stack.push(test);
|
|
10096
|
+
break;
|
|
10097
|
+
}
|
|
10098
|
+
case "BinaryExpression":
|
|
10099
|
+
if (node.operator === "has" &&
|
|
10100
|
+
node.right.type === "UnaryExpression" &&
|
|
10101
|
+
node.right.operator === ":") {
|
|
10102
|
+
const [left, right] = istate.stack.slice(-2);
|
|
10103
|
+
if (left.embeddedEffects ||
|
|
10104
|
+
right.embeddedEffects ||
|
|
10105
|
+
!hasValue(left.value) ||
|
|
10106
|
+
!hasValue(right.value) ||
|
|
10107
|
+
!(left.value.type & (TypeTag.Module | TypeTag.Class))) {
|
|
10108
|
+
break;
|
|
10109
|
+
}
|
|
10110
|
+
const id = node.right.argument;
|
|
10111
|
+
if ((0,external_util_cjs_namespaceObject.every)(left.value.value, (m) => {
|
|
10112
|
+
if (hasProperty(m.decls, id.name))
|
|
10113
|
+
return false;
|
|
10114
|
+
// This is overkill, since we've already looked up
|
|
10115
|
+
// node.left, but the actual lookup rules are complicated,
|
|
10116
|
+
// and embedded within state.lookup; so just defer to that.
|
|
10117
|
+
return (istate.state.lookup({
|
|
10118
|
+
type: "MemberExpression",
|
|
10119
|
+
object: node.left,
|
|
10120
|
+
property: id,
|
|
10121
|
+
computed: false,
|
|
10122
|
+
})[1] == null);
|
|
10123
|
+
})) {
|
|
10124
|
+
popIstate(istate, node.right);
|
|
10125
|
+
popIstate(istate, node.left);
|
|
10126
|
+
const rep = wrap({ type: "Literal", value: false, raw: "false" }, node.loc);
|
|
10127
|
+
istate.stack.push({
|
|
10128
|
+
value: { type: TypeTag.False },
|
|
10129
|
+
embeddedEffects: false,
|
|
10130
|
+
node: rep,
|
|
10131
|
+
});
|
|
10132
|
+
return rep;
|
|
10133
|
+
}
|
|
10134
|
+
}
|
|
10135
|
+
else {
|
|
10136
|
+
const rep = tryCommuteAndAssociate(istate, node);
|
|
10137
|
+
if (rep)
|
|
10138
|
+
return rep;
|
|
10139
|
+
const level = istate.state.config?.checkTypes === "OFF"
|
|
10140
|
+
? null
|
|
10141
|
+
: istate.state.config?.checkTypes || "WARNING";
|
|
10142
|
+
if (!level)
|
|
10143
|
+
break;
|
|
10144
|
+
if (node.operator === "==" || node.operator === "!=") {
|
|
10145
|
+
const [{ value: left }, { value: right }] = istate.stack.slice(-2);
|
|
10146
|
+
if ((left.type === TypeTag.Null && !(right.type & TypeTag.Null)) ||
|
|
10147
|
+
(right.type === TypeTag.Null && !(left.type & TypeTag.Null))) {
|
|
10148
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(istate.state, node.loc, `This comparison seems redundant because ${(0,external_api_cjs_namespaceObject.formatAst)(left.type === TypeTag.Null ? node.right : node.left)} should never be null`, level);
|
|
10149
|
+
}
|
|
10150
|
+
}
|
|
10151
|
+
else if (node.operator === "as") {
|
|
10152
|
+
const [{ value: left }] = istate.stack.slice(-1);
|
|
10153
|
+
const right = typeFromTypespec(istate.state, node.right);
|
|
10154
|
+
if (!couldBe(left, right)) {
|
|
10155
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(istate.state, node.loc, `The type ${display(left)} cannot be converted to ${display(right)} because they have nothing in common`, level);
|
|
10156
|
+
}
|
|
10157
|
+
}
|
|
10158
|
+
}
|
|
10159
|
+
break;
|
|
10160
|
+
case "LogicalExpression": {
|
|
10161
|
+
const [left, right] = istate.stack.slice(-2);
|
|
10162
|
+
const isAnd = node.operator === "&&" || node.operator === "and";
|
|
10163
|
+
if (isAnd ? mustBeFalse(left.value) : mustBeTrue(left.value)) {
|
|
10164
|
+
popIstate(istate, node.right);
|
|
10165
|
+
return node.left;
|
|
10166
|
+
}
|
|
10167
|
+
if (
|
|
10168
|
+
// bail if left could be anything other than bool/integer
|
|
10169
|
+
(left.value.type &
|
|
10170
|
+
(TypeTag.Boolean | TypeTag.Number | TypeTag.Long)) !==
|
|
10171
|
+
left.value.type ||
|
|
10172
|
+
// bail if left could be boolean AND it could be integer
|
|
10173
|
+
(left.value.type & TypeTag.Boolean &&
|
|
10174
|
+
left.value.type & (TypeTag.Number | TypeTag.Long)) ||
|
|
10175
|
+
// bail if right doesn't match left
|
|
10176
|
+
(right.value.type &
|
|
10177
|
+
(left.value.type & TypeTag.Boolean
|
|
10178
|
+
? TypeTag.Boolean
|
|
10179
|
+
: TypeTag.Number | TypeTag.Long)) !==
|
|
10180
|
+
right.value.type) {
|
|
10181
|
+
break;
|
|
10182
|
+
}
|
|
10183
|
+
if (right.value.type === (isAnd ? TypeTag.True : TypeTag.False) &&
|
|
10184
|
+
!right.embeddedEffects &&
|
|
10185
|
+
(left.value.type & TypeTag.Boolean) === left.value.type) {
|
|
10186
|
+
popIstate(istate, node.right);
|
|
10187
|
+
return node.left;
|
|
10188
|
+
}
|
|
10189
|
+
if (isAnd ? !mustBeTrue(left.value) : !mustBeFalse(left.value)) {
|
|
10190
|
+
break;
|
|
10191
|
+
}
|
|
10192
|
+
if (!(left.value.type & ~TypeTag.Boolean)) {
|
|
10193
|
+
// the left type is boolean, so the
|
|
10194
|
+
// `&` or `|` would be a no-op. We
|
|
10195
|
+
// need to check that its side-effect
|
|
10196
|
+
// free. Just cheap checks for now.
|
|
10197
|
+
if (!left.embeddedEffects) {
|
|
10198
|
+
popIstate(istate, node.right);
|
|
10199
|
+
popIstate(istate, node.left);
|
|
10200
|
+
istate.stack.push(right);
|
|
10201
|
+
return node.right;
|
|
10202
|
+
}
|
|
10203
|
+
}
|
|
10204
|
+
const rep = node;
|
|
10205
|
+
rep.type = "BinaryExpression";
|
|
10206
|
+
rep.operator = isAnd ? "&" : "|";
|
|
10207
|
+
break;
|
|
10208
|
+
}
|
|
10209
|
+
}
|
|
10210
|
+
return null;
|
|
10211
|
+
}
|
|
10212
|
+
function afterEvaluate(istate, node) {
|
|
10213
|
+
switch (node.type) {
|
|
10214
|
+
case "IfStatement":
|
|
10215
|
+
if (node.alternate &&
|
|
10216
|
+
node.alternate.type === "BlockStatement" &&
|
|
10217
|
+
!node.alternate.body.length) {
|
|
10218
|
+
delete node.alternate;
|
|
10219
|
+
}
|
|
10220
|
+
break;
|
|
10221
|
+
}
|
|
10222
|
+
if (isExpression(node) && node.type !== "Literal") {
|
|
10223
|
+
const top = istate.stack[istate.stack.length - 1];
|
|
10224
|
+
if (!top.embeddedEffects && hasValue(top.value)) {
|
|
10225
|
+
const rep = mcExprFromType(top.value);
|
|
10226
|
+
if (rep) {
|
|
10227
|
+
top.node = rep;
|
|
10228
|
+
return withLoc(rep, node, node);
|
|
10229
|
+
}
|
|
10230
|
+
}
|
|
10231
|
+
}
|
|
10232
|
+
return null;
|
|
10233
|
+
}
|
|
10234
|
+
function identity(istate, node, left, right, allowedTypes, target) {
|
|
10235
|
+
if (hasValue(right.value) &&
|
|
10236
|
+
right.value.type & allowedTypes &&
|
|
10237
|
+
!(left.value.type & ~allowedTypes) &&
|
|
10238
|
+
Number(right.value.value) === target) {
|
|
10239
|
+
// a +/- 0 => a
|
|
10240
|
+
// but we still need to check that the type of the zero
|
|
10241
|
+
// doesn't change the type of the result.
|
|
10242
|
+
if (right.value.type === TypeTag.Number ||
|
|
10243
|
+
(right.value.type === TypeTag.Long &&
|
|
10244
|
+
!(left.value.type & ~(TypeTag.Long | TypeTag.Double))) ||
|
|
10245
|
+
(right.value.type === TypeTag.Float &&
|
|
10246
|
+
!(left.value.type & ~(TypeTag.Float | TypeTag.Double))) ||
|
|
10247
|
+
(right.value.type === TypeTag.Double &&
|
|
10248
|
+
left.value.type === TypeTag.Double)) {
|
|
10249
|
+
istate.stack.pop();
|
|
10250
|
+
return node.left;
|
|
10251
|
+
}
|
|
10252
|
+
}
|
|
10253
|
+
return null;
|
|
10254
|
+
}
|
|
10255
|
+
function zero(istate, node, left, right, allowedTypes, target) {
|
|
10256
|
+
if (hasValue(right.value) &&
|
|
10257
|
+
right.value.type & allowedTypes &&
|
|
10258
|
+
!(left.value.type & ~allowedTypes) &&
|
|
10259
|
+
Number(right.value.value) === target) {
|
|
10260
|
+
// a * 0 => 0
|
|
10261
|
+
// but we still need to check that the type of a
|
|
10262
|
+
// doesn't change the type of the zero.
|
|
10263
|
+
if ((right.value.type === TypeTag.Number &&
|
|
10264
|
+
left.value.type === TypeTag.Number) ||
|
|
10265
|
+
(right.value.type === TypeTag.Long &&
|
|
10266
|
+
!(left.value.type & ~(TypeTag.Long | TypeTag.Number))) ||
|
|
10267
|
+
(right.value.type === TypeTag.Float &&
|
|
10268
|
+
!(left.value.type & ~(TypeTag.Float | TypeTag.Number))) ||
|
|
10269
|
+
right.value.type === TypeTag.Double) {
|
|
10270
|
+
istate.stack.splice(-2, 1);
|
|
10271
|
+
return node.right;
|
|
10272
|
+
}
|
|
10273
|
+
}
|
|
10274
|
+
return null;
|
|
10275
|
+
}
|
|
10276
|
+
function tryIdentity(istate, node, left, right) {
|
|
10277
|
+
switch (node.operator) {
|
|
10278
|
+
case "+":
|
|
10279
|
+
case "-": {
|
|
10280
|
+
const rep = identity(istate, node, left, right, TypeTag.Numeric, 0);
|
|
10281
|
+
if (rep)
|
|
10282
|
+
return rep;
|
|
10283
|
+
if (node.right.type === "UnaryExpression" &&
|
|
10284
|
+
node.right.operator === "-") {
|
|
10285
|
+
// We can convert a +/- -b to a -/+ b, but we have to know
|
|
10286
|
+
// something about the types. eg if b is Number, with the
|
|
10287
|
+
// value -2^31, negating b will leave the value as -2^31.
|
|
10288
|
+
// This doesn't matter if the a is also Number, but if
|
|
10289
|
+
// a might be Long, Float or Double, we would change the
|
|
10290
|
+
// result. Similarly for Long and -2^63
|
|
10291
|
+
if (!((left.value.type | right.value.type) & ~TypeTag.Numeric) &&
|
|
10292
|
+
(right.value.type & TypeTag.Number // Negating -2^31 goes wrong if left is a wider type
|
|
10293
|
+
? !(left.value.type &
|
|
10294
|
+
(TypeTag.Long | TypeTag.Float | TypeTag.Double))
|
|
10295
|
+
: right.value.type & TypeTag.Long // Negating -2^63 goes wrong if left is a float/double
|
|
10296
|
+
? !(left.value.type & (TypeTag.Float | TypeTag.Double))
|
|
10297
|
+
: true)) {
|
|
10298
|
+
right.value = evaluateBinaryTypes("-", { type: TypeTag.Number, value: 0 }, right.value);
|
|
10299
|
+
right.node = node.right.argument;
|
|
10300
|
+
node.right = right.node;
|
|
10301
|
+
node.operator = node.operator === "+" ? "-" : "+";
|
|
10302
|
+
}
|
|
10303
|
+
}
|
|
10304
|
+
break;
|
|
10305
|
+
}
|
|
10306
|
+
case "*": {
|
|
10307
|
+
const rep = zero(istate, node, left, right, TypeTag.Numeric, 0);
|
|
10308
|
+
if (rep)
|
|
10309
|
+
return rep;
|
|
10310
|
+
// fall through
|
|
10311
|
+
}
|
|
10312
|
+
case "/": {
|
|
10313
|
+
const rep = identity(istate, node, left, right, TypeTag.Numeric, 1);
|
|
10314
|
+
if (rep)
|
|
10315
|
+
return rep;
|
|
10316
|
+
break;
|
|
10317
|
+
}
|
|
10318
|
+
case "|": {
|
|
10319
|
+
const rep = zero(istate, node, left, right, TypeTag.Number | TypeTag.Long, -1);
|
|
10320
|
+
if (rep)
|
|
10321
|
+
return rep;
|
|
10322
|
+
// fall through
|
|
10323
|
+
}
|
|
10324
|
+
case "^": {
|
|
10325
|
+
const rep = identity(istate, node, left, right, TypeTag.Number | TypeTag.Long, 0);
|
|
10326
|
+
if (rep)
|
|
10327
|
+
return rep;
|
|
10328
|
+
break;
|
|
10329
|
+
}
|
|
10330
|
+
case "&": {
|
|
10331
|
+
const rep = zero(istate, node, left, right, TypeTag.Number | TypeTag.Long, 0) ||
|
|
10332
|
+
identity(istate, node, left, right, TypeTag.Number | TypeTag.Long, -1);
|
|
10333
|
+
if (rep)
|
|
10334
|
+
return rep;
|
|
10335
|
+
break;
|
|
10336
|
+
}
|
|
10337
|
+
}
|
|
10338
|
+
return null;
|
|
10339
|
+
}
|
|
10340
|
+
function tryCommuteAndAssociate(istate, node) {
|
|
10341
|
+
let [left, right] = istate.stack.slice(-2);
|
|
10342
|
+
// no need to do anything if both sides are constants
|
|
10343
|
+
if (!right || (hasValue(left.value) && hasValue(right.value))) {
|
|
10344
|
+
return null;
|
|
10345
|
+
}
|
|
10346
|
+
switch (node.operator) {
|
|
10347
|
+
case "+":
|
|
10348
|
+
// Addition is only commutative/associative if both arguments
|
|
10349
|
+
// are numeric, or one argument is Number, and the other is Char
|
|
10350
|
+
if (left.value.type & ~(TypeTag.Numeric | TypeTag.Char) ||
|
|
10351
|
+
right.value.type & ~(TypeTag.Numeric | TypeTag.Char) ||
|
|
10352
|
+
left.value.type & right.value.type & TypeTag.Char) {
|
|
10353
|
+
break;
|
|
10354
|
+
}
|
|
10355
|
+
// fallthrough
|
|
10356
|
+
case "*":
|
|
10357
|
+
case "&":
|
|
10358
|
+
case "|":
|
|
10359
|
+
case "^":
|
|
10360
|
+
// flip the left argument to the right if the left has
|
|
10361
|
+
// a known value, but the right does not, or if the
|
|
10362
|
+
// top operator is additive, and the left operand is
|
|
10363
|
+
// negated, and the right operand is not.
|
|
10364
|
+
if (!left.embeddedEffects &&
|
|
10365
|
+
(hasValue(left.value) ||
|
|
10366
|
+
(!right.embeddedEffects &&
|
|
10367
|
+
node.operator === "+" &&
|
|
10368
|
+
node.left.type === "UnaryExpression" &&
|
|
10369
|
+
node.left.operator === "-" &&
|
|
10370
|
+
(node.right.type !== "UnaryExpression" ||
|
|
10371
|
+
node.right.operator !== "-")))) {
|
|
10372
|
+
const l = node.left;
|
|
10373
|
+
node.left = node.right;
|
|
10374
|
+
node.right = l;
|
|
10375
|
+
istate.stack.splice(-2, 2, right, left);
|
|
10376
|
+
const r = right;
|
|
10377
|
+
right = left;
|
|
10378
|
+
left = r;
|
|
10379
|
+
}
|
|
10380
|
+
// fallthrough
|
|
10381
|
+
case "-":
|
|
10382
|
+
if (tryReAssociate(istate, node, left, right)) {
|
|
10383
|
+
[left, right] = istate.stack.slice(-2);
|
|
10384
|
+
}
|
|
10385
|
+
}
|
|
10386
|
+
return tryIdentity(istate, node, left, right);
|
|
10387
|
+
}
|
|
10388
|
+
/*
|
|
10389
|
+
* Try to reorder (a op K1) op K2 => a op (K1 op K2),
|
|
10390
|
+
* and fold K1 op K2.
|
|
10391
|
+
*
|
|
10392
|
+
* Failing that,
|
|
10393
|
+
* Try to reorder (a op K1) op (b op K2) as
|
|
10394
|
+
* (a op b) op (K1 op K2), and fold K1 op K2.
|
|
10395
|
+
*
|
|
10396
|
+
* Failing that,
|
|
10397
|
+
* Try to reorder (a op K) op b => (a op b) op K
|
|
10398
|
+
* so that constants float up and to the right.
|
|
10399
|
+
* This helps because now ((a op K1) op b) op K2
|
|
10400
|
+
* becomes ((a op b) op K1) op K2, and we can
|
|
10401
|
+
* fold K1 op K2 by the first transformation.
|
|
10402
|
+
*
|
|
10403
|
+
* Floating point arithmetic isn't really associative
|
|
10404
|
+
* though, so we mostly suppress this when Floats
|
|
10405
|
+
* and Doubles may be involved; except that
|
|
10406
|
+
* (a + K1) + K2 can be safely converted to
|
|
10407
|
+
* a + (K1 + K2) if K1 and K2 have the same sign.
|
|
10408
|
+
*/
|
|
10409
|
+
function tryReAssociate(istate, node, left, right) {
|
|
10410
|
+
if (node.left.type !== "BinaryExpression" ||
|
|
10411
|
+
(node.left.operator !== node.operator &&
|
|
10412
|
+
!(node.operator === "+" && node.left.operator === "-") &&
|
|
10413
|
+
!(node.operator === "-" && node.left.operator === "+"))) {
|
|
10414
|
+
return false;
|
|
10415
|
+
}
|
|
10416
|
+
const lr = ast_getLiteralNode(node.left.right);
|
|
10417
|
+
if (!lr)
|
|
10418
|
+
return false;
|
|
10419
|
+
const leftRight = interp_evaluate(istate, lr);
|
|
10420
|
+
if (!hasValue(leftRight.value))
|
|
10421
|
+
return false;
|
|
10422
|
+
if (hasValue(right.value)) {
|
|
10423
|
+
// (ll + lr) + r => ll + (r + lr)
|
|
10424
|
+
// (ll - lr) - r => ll - (r + lr)
|
|
10425
|
+
// (ll + lr) - r => ll + (r - lr)
|
|
10426
|
+
// (ll - lr) + r => ll - (r - lr)
|
|
10427
|
+
const tmpNode = {
|
|
10428
|
+
type: "BinaryExpression",
|
|
10429
|
+
operator: node.operator === "+" || node.operator === "-"
|
|
10430
|
+
? node.operator === node.left.operator
|
|
10431
|
+
? "+"
|
|
10432
|
+
: "-"
|
|
10433
|
+
: node.operator,
|
|
10434
|
+
left: node.right,
|
|
10435
|
+
right: node.left.right,
|
|
10436
|
+
};
|
|
10437
|
+
if (tmpNode.operator === "+" || tmpNode.operator === "-") {
|
|
10438
|
+
if (leftRight.value.type & (TypeTag.Float | TypeTag.Double) ||
|
|
10439
|
+
right.value.type & (TypeTag.Float | TypeTag.Double)) {
|
|
10440
|
+
// we don't want to fold "a + 1.0 - 1.0" because
|
|
10441
|
+
// it could be there for rounding purposes
|
|
10442
|
+
const lsign = right.value.value < 0;
|
|
10443
|
+
const rsign = leftRight.value.value < 0 === (tmpNode.operator === "+");
|
|
10444
|
+
if (lsign !== rsign)
|
|
10445
|
+
return false;
|
|
10446
|
+
}
|
|
10447
|
+
}
|
|
10448
|
+
const repType = interp_evaluate(istate, tmpNode);
|
|
10449
|
+
if (!hasValue(repType.value))
|
|
10450
|
+
return false;
|
|
10451
|
+
const repNode = mcExprFromType(repType.value);
|
|
10452
|
+
if (!repNode)
|
|
10453
|
+
return false;
|
|
10454
|
+
left.node = node.left = node.left.left;
|
|
10455
|
+
node.right = repNode;
|
|
10456
|
+
istate.stack.splice(-1, 1, repType);
|
|
10457
|
+
repType.node = repNode;
|
|
10458
|
+
return true;
|
|
10459
|
+
}
|
|
10460
|
+
if (leftRight.value.type !== left.value.type ||
|
|
10461
|
+
leftRight.value.type !== right.value.type ||
|
|
10462
|
+
leftRight.value.type & (TypeTag.Float | TypeTag.Double)) {
|
|
10463
|
+
return false;
|
|
10464
|
+
}
|
|
10465
|
+
if (node.right.type === "BinaryExpression" &&
|
|
10466
|
+
(node.right.operator === node.operator ||
|
|
10467
|
+
((node.operator === "+" || node.operator === "-") &&
|
|
10468
|
+
(node.right.operator === "+" || node.right.operator === "-")))) {
|
|
10469
|
+
// (a + K1) + (b + K2) => (a + b) + (K1 + K2)
|
|
10470
|
+
const rr = ast_getLiteralNode(node.right.right);
|
|
10471
|
+
if (!rr)
|
|
10472
|
+
return false;
|
|
10473
|
+
const rightRight = interp_evaluate(istate, rr);
|
|
10474
|
+
if (!hasValue(rightRight.value))
|
|
10475
|
+
return false;
|
|
10476
|
+
const rightOp = node.operator === "+" || node.operator === "-"
|
|
10477
|
+
? ((node.left.operator === "+") === (node.right.operator === "+")) ===
|
|
10478
|
+
(node.operator === "+")
|
|
10479
|
+
? "+"
|
|
10480
|
+
: "-"
|
|
10481
|
+
: node.operator;
|
|
10482
|
+
const topOp = node.left.operator;
|
|
10483
|
+
const leftOp = node.operator;
|
|
10484
|
+
const rightType = evaluateBinaryTypes(rightOp, leftRight.value, rightRight.value);
|
|
10485
|
+
if (!hasValue(rightType))
|
|
10486
|
+
return false;
|
|
10487
|
+
const repNode = mcExprFromType(rightType);
|
|
10488
|
+
if (!repNode)
|
|
10489
|
+
return false;
|
|
10490
|
+
node.left.right = node.right.left;
|
|
10491
|
+
node.right = repNode;
|
|
10492
|
+
node.left.operator = leftOp;
|
|
10493
|
+
node.operator = topOp;
|
|
10494
|
+
istate.stack.splice(-1, 1, {
|
|
10495
|
+
value: rightType,
|
|
10496
|
+
node: repNode,
|
|
10497
|
+
embeddedEffects: false,
|
|
10498
|
+
});
|
|
10499
|
+
return true;
|
|
10500
|
+
}
|
|
10501
|
+
const op = node.operator;
|
|
10502
|
+
node.operator = node.left.operator;
|
|
10503
|
+
node.left.operator = op;
|
|
10504
|
+
leftRight.node = node.left.right;
|
|
10505
|
+
node.left.right = node.right;
|
|
10506
|
+
node.right = leftRight.node;
|
|
10507
|
+
istate.stack.splice(-1, 1, leftRight);
|
|
10508
|
+
return true;
|
|
10509
|
+
}
|
|
10510
|
+
|
|
10511
|
+
;// CONCATENATED MODULE: ./src/unused-exprs.ts
|
|
10512
|
+
|
|
10513
|
+
|
|
10514
|
+
|
|
10515
|
+
function cleanupUnusedVars(state, node) {
|
|
10516
|
+
const [parent] = state.stack.slice(-1);
|
|
10517
|
+
if (parent.node !== node) {
|
|
10518
|
+
return;
|
|
10519
|
+
}
|
|
10520
|
+
if (parent.type != "BlockStatement") {
|
|
10521
|
+
throw new Error(`Unexpected parent type '${parent.type}' for local declaration`);
|
|
10522
|
+
}
|
|
10523
|
+
if (parent.decls) {
|
|
10524
|
+
let toRemove = null;
|
|
10525
|
+
Object.values(parent.decls).forEach((decls) => {
|
|
10526
|
+
if (decls.length === 1 &&
|
|
10527
|
+
decls[0].type === "VariableDeclarator" &&
|
|
10528
|
+
!decls[0].used) {
|
|
10529
|
+
if (!toRemove)
|
|
10530
|
+
toRemove = {};
|
|
10531
|
+
toRemove[decls[0].name] = decls[0];
|
|
10532
|
+
}
|
|
10533
|
+
});
|
|
10534
|
+
if (toRemove) {
|
|
10535
|
+
const varDeclarations = new Map();
|
|
10536
|
+
const stack = [];
|
|
10537
|
+
traverseAst(node, (node) => {
|
|
10538
|
+
switch (node.type) {
|
|
10539
|
+
case "SwitchCase":
|
|
10540
|
+
stack.push(node.consequent);
|
|
10541
|
+
break;
|
|
10542
|
+
case "BlockStatement":
|
|
10543
|
+
stack.push(node.body);
|
|
10544
|
+
break;
|
|
10545
|
+
}
|
|
10546
|
+
}, (node) => {
|
|
10547
|
+
switch (node.type) {
|
|
10548
|
+
case "SwitchCase":
|
|
10549
|
+
case "BlockStatement":
|
|
10550
|
+
stack.pop();
|
|
10551
|
+
break;
|
|
10552
|
+
case "VariableDeclaration": {
|
|
10553
|
+
node.declarations.forEach((decl, i) => {
|
|
10554
|
+
const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(decl.id);
|
|
10555
|
+
if (hasProperty(toRemove, name)) {
|
|
10556
|
+
const info = varDeclarations.get(node);
|
|
10557
|
+
if (info) {
|
|
10558
|
+
info.indices.push(i);
|
|
10559
|
+
}
|
|
10560
|
+
else {
|
|
10561
|
+
varDeclarations.set(node, {
|
|
10562
|
+
parent: stack[stack.length - 1],
|
|
10563
|
+
indices: [i],
|
|
10564
|
+
});
|
|
10565
|
+
}
|
|
10566
|
+
}
|
|
10567
|
+
});
|
|
10568
|
+
break;
|
|
7588
10569
|
}
|
|
7589
10570
|
case "ExpressionStatement":
|
|
7590
10571
|
if (node.expression.type === "AssignmentExpression") {
|
|
@@ -7709,6 +10690,9 @@ function cleanupUnusedVars(state, node) {
|
|
|
7709
10690
|
|
|
7710
10691
|
|
|
7711
10692
|
|
|
10693
|
+
|
|
10694
|
+
|
|
10695
|
+
|
|
7712
10696
|
function collectClassInfo(state) {
|
|
7713
10697
|
const toybox = state.stack[0].decls["Toybox"][0];
|
|
7714
10698
|
const lang = toybox.decls["Lang"][0];
|
|
@@ -7786,7 +10770,7 @@ function collectClassInfo(state) {
|
|
|
7786
10770
|
funcs.forEach((f) => {
|
|
7787
10771
|
if (f.type === "FunctionDeclaration" &&
|
|
7788
10772
|
!(f.attributes & StateNodeAttributes.STATIC)) {
|
|
7789
|
-
(0,external_api_cjs_namespaceObject.markInvokeClassMethod)(f);
|
|
10773
|
+
(0,external_api_cjs_namespaceObject.markInvokeClassMethod)(state, f);
|
|
7790
10774
|
}
|
|
7791
10775
|
});
|
|
7792
10776
|
});
|
|
@@ -7866,7 +10850,7 @@ async function analyze(fnMap, resourcesMap, manifestXML, config) {
|
|
|
7866
10850
|
if (scope.type == "FunctionDeclaration") {
|
|
7867
10851
|
if (markApi) {
|
|
7868
10852
|
node.body = null;
|
|
7869
|
-
scope.info = (0,external_api_cjs_namespaceObject.getApiFunctionInfo)(scope);
|
|
10853
|
+
scope.info = (0,external_api_cjs_namespaceObject.getApiFunctionInfo)(state, scope);
|
|
7870
10854
|
delete scope.stack;
|
|
7871
10855
|
}
|
|
7872
10856
|
const allFuncs = state.allFunctions;
|
|
@@ -7944,7 +10928,7 @@ function reportMissingSymbols(state, config) {
|
|
|
7944
10928
|
return false;
|
|
7945
10929
|
});
|
|
7946
10930
|
})) {
|
|
7947
|
-
diagnostic(state, node.loc, `The expression ${(0,external_api_cjs_namespaceObject.formatAst)(node)} will fail at runtime using sdk-4.1.6`, compiler2DiagnosticType);
|
|
10931
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, node.loc, `The expression ${(0,external_api_cjs_namespaceObject.formatAst)(node)} will fail at runtime using sdk-4.1.6`, compiler2DiagnosticType);
|
|
7948
10932
|
}
|
|
7949
10933
|
return undefined;
|
|
7950
10934
|
}
|
|
@@ -7954,7 +10938,7 @@ function reportMissingSymbols(state, config) {
|
|
|
7954
10938
|
return undefined;
|
|
7955
10939
|
}
|
|
7956
10940
|
}
|
|
7957
|
-
diagnostic(state, node.loc, `Undefined symbol ${nodeStr || (0,external_api_cjs_namespaceObject.formatAst)(node)}`, diagnosticType);
|
|
10941
|
+
(0,external_api_cjs_namespaceObject.diagnostic)(state, node.loc, `Undefined symbol ${nodeStr || (0,external_api_cjs_namespaceObject.formatAst)(node)}`, diagnosticType);
|
|
7958
10942
|
return false;
|
|
7959
10943
|
});
|
|
7960
10944
|
};
|
|
@@ -7993,405 +10977,53 @@ function getLiteralFromDecls(lookupDefns) {
|
|
|
7993
10977
|
}
|
|
7994
10978
|
return null;
|
|
7995
10979
|
}
|
|
7996
|
-
function
|
|
7997
|
-
if (
|
|
7998
|
-
return null;
|
|
7999
|
-
if (node.type == "Literal")
|
|
8000
|
-
return node;
|
|
8001
|
-
if (node.type == "BinaryExpression" && node.operator == "as") {
|
|
8002
|
-
return getLiteralNode(node.left) && node;
|
|
8003
|
-
}
|
|
8004
|
-
if (node.type == "UnaryExpression") {
|
|
8005
|
-
if (node.argument.type != "Literal")
|
|
8006
|
-
return null;
|
|
8007
|
-
switch (node.operator) {
|
|
8008
|
-
case "-": {
|
|
8009
|
-
const [arg, type] = getNodeValue(node.argument);
|
|
8010
|
-
if (type === "Number" || type === "Long") {
|
|
8011
|
-
return replacementLiteral(node, -arg.value, type);
|
|
8012
|
-
}
|
|
8013
|
-
}
|
|
8014
|
-
}
|
|
8015
|
-
}
|
|
8016
|
-
return null;
|
|
8017
|
-
}
|
|
8018
|
-
function fullTypeName(state, tsp) {
|
|
8019
|
-
if (typeof tsp.name === "string") {
|
|
8020
|
-
return tsp.name;
|
|
8021
|
-
}
|
|
8022
|
-
const [, results] = state.lookupType(tsp.name);
|
|
8023
|
-
if (results && results.length === 1 && results[0].results.length === 1) {
|
|
8024
|
-
const result = results[0].results[0];
|
|
8025
|
-
if ((0,external_api_cjs_namespaceObject.isStateNode)(result)) {
|
|
8026
|
-
return result.fullName;
|
|
8027
|
-
}
|
|
8028
|
-
}
|
|
8029
|
-
return null;
|
|
8030
|
-
}
|
|
8031
|
-
function isBooleanExpression(state, node) {
|
|
8032
|
-
switch (node.type) {
|
|
8033
|
-
case "Literal":
|
|
8034
|
-
return typeof node.value === "boolean";
|
|
8035
|
-
case "BinaryExpression":
|
|
8036
|
-
switch (node.operator) {
|
|
8037
|
-
case "==":
|
|
8038
|
-
case "!=":
|
|
8039
|
-
case "<=":
|
|
8040
|
-
case ">=":
|
|
8041
|
-
case "<":
|
|
8042
|
-
case ">":
|
|
8043
|
-
return true;
|
|
8044
|
-
case "as":
|
|
8045
|
-
return node.right.ts.length === 1 &&
|
|
8046
|
-
node.right.ts[0].type === "TypeSpecPart" &&
|
|
8047
|
-
node.right.ts[0].name &&
|
|
8048
|
-
fullTypeName(state, node.right.ts[0]) === "$.Toybox.Lang.Boolean"
|
|
8049
|
-
? true
|
|
8050
|
-
: false;
|
|
8051
|
-
}
|
|
8052
|
-
return false;
|
|
8053
|
-
case "LogicalExpression":
|
|
8054
|
-
return (isBooleanExpression(state, node.left) &&
|
|
8055
|
-
isBooleanExpression(state, node.right));
|
|
8056
|
-
case "UnaryExpression":
|
|
8057
|
-
return node.operator === "!" && isBooleanExpression(state, node.argument);
|
|
8058
|
-
}
|
|
8059
|
-
return false;
|
|
8060
|
-
}
|
|
8061
|
-
function roundToFloat(value) {
|
|
8062
|
-
return new Float32Array([value])[0];
|
|
8063
|
-
}
|
|
8064
|
-
function replacementLiteral(arg, value, type) {
|
|
8065
|
-
if (value === null) {
|
|
8066
|
-
type = "Null";
|
|
8067
|
-
}
|
|
8068
|
-
else if (typeof value === "boolean") {
|
|
8069
|
-
type = "Boolean";
|
|
8070
|
-
}
|
|
8071
|
-
else if (type === "Number") {
|
|
8072
|
-
value = Number(BigInt.asIntN(32, BigInt(value)));
|
|
8073
|
-
}
|
|
8074
|
-
else if (type === "Long") {
|
|
8075
|
-
value = BigInt.asIntN(64, BigInt(value));
|
|
8076
|
-
}
|
|
8077
|
-
else if (type === "Float") {
|
|
8078
|
-
value = roundToFloat(Number(value));
|
|
8079
|
-
}
|
|
8080
|
-
let raw = type === "String"
|
|
8081
|
-
? JSON.stringify(value)
|
|
8082
|
-
: type === "Char"
|
|
8083
|
-
? value === "'"
|
|
8084
|
-
? "'\\''"
|
|
8085
|
-
: "'" + JSON.stringify(value).slice(1, -1) + "'"
|
|
8086
|
-
: value == null
|
|
8087
|
-
? "null"
|
|
8088
|
-
: value.toString();
|
|
8089
|
-
if (type === "Long") {
|
|
8090
|
-
raw += "l";
|
|
8091
|
-
}
|
|
8092
|
-
else if (type === "Double") {
|
|
8093
|
-
raw += "d";
|
|
8094
|
-
}
|
|
8095
|
-
else if (type === "Float") {
|
|
8096
|
-
if (prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.test(raw)) {
|
|
8097
|
-
raw += "f";
|
|
8098
|
-
}
|
|
8099
|
-
else {
|
|
8100
|
-
const match = raw.match(/^(-)?(\d*)\.(\d+)(e\d+)?/);
|
|
8101
|
-
if (match && match[2].length + match[3].length > 9) {
|
|
8102
|
-
for (let l = 9 - match[2].length; l > 0; l--) {
|
|
8103
|
-
const s = `${match[1] || ""}${match[2]}.${match[3].substring(0, l)}${match[4] || ""}`;
|
|
8104
|
-
if (value !== roundToFloat(parseFloat(s)))
|
|
8105
|
-
break;
|
|
8106
|
-
raw = s;
|
|
8107
|
-
}
|
|
8108
|
-
}
|
|
8109
|
-
}
|
|
8110
|
-
}
|
|
8111
|
-
const { start, end, loc } = arg;
|
|
8112
|
-
return {
|
|
8113
|
-
type: "Literal",
|
|
8114
|
-
value,
|
|
8115
|
-
raw,
|
|
8116
|
-
start,
|
|
8117
|
-
end,
|
|
8118
|
-
loc,
|
|
8119
|
-
};
|
|
8120
|
-
}
|
|
8121
|
-
function classify(arg) {
|
|
8122
|
-
switch (arg) {
|
|
8123
|
-
case "Number":
|
|
8124
|
-
return { big: false, int: true };
|
|
8125
|
-
case "Long":
|
|
8126
|
-
return { big: true, int: true };
|
|
8127
|
-
case "Float":
|
|
8128
|
-
return { big: false, int: false };
|
|
8129
|
-
case "Double":
|
|
8130
|
-
return { big: true, int: false };
|
|
8131
|
-
}
|
|
8132
|
-
return null;
|
|
8133
|
-
}
|
|
8134
|
-
function common_arith_types(left, right) {
|
|
8135
|
-
const l = classify(left);
|
|
8136
|
-
if (!l)
|
|
8137
|
-
return null;
|
|
8138
|
-
const r = classify(right);
|
|
8139
|
-
if (!r)
|
|
8140
|
-
return null;
|
|
8141
|
-
if (l.big || r.big) {
|
|
8142
|
-
return l.int && r.int
|
|
8143
|
-
? ["Long", (v) => BigInt.asIntN(64, BigInt(v))]
|
|
8144
|
-
: ["Double", (v) => Number(v)];
|
|
8145
|
-
}
|
|
8146
|
-
else {
|
|
8147
|
-
return l.int && r.int
|
|
8148
|
-
? ["Number", (v) => BigInt.asIntN(32, BigInt(v))]
|
|
8149
|
-
: ["Float", (v) => roundToFloat(Number(v))];
|
|
8150
|
-
}
|
|
8151
|
-
}
|
|
8152
|
-
function common_bitwise_types(left, right) {
|
|
8153
|
-
if (left === "Boolean" && right === "Boolean") {
|
|
8154
|
-
return ["Boolean", (v) => (v ? true : false)];
|
|
8155
|
-
}
|
|
8156
|
-
const l = classify(left);
|
|
8157
|
-
if (!l)
|
|
8158
|
-
return null;
|
|
8159
|
-
const r = classify(right);
|
|
8160
|
-
if (!r)
|
|
10980
|
+
function optimizeNode(istate, node) {
|
|
10981
|
+
if (istate.state.inlining)
|
|
8161
10982
|
return null;
|
|
8162
|
-
if (
|
|
8163
|
-
return null;
|
|
8164
|
-
return l.big || r.big
|
|
8165
|
-
? ["Long", (v) => BigInt.asIntN(64, BigInt(v))]
|
|
8166
|
-
: ["Number", (v) => Number(BigInt.asIntN(32, BigInt(v)))];
|
|
8167
|
-
}
|
|
8168
|
-
function plus_types(left, right) {
|
|
8169
|
-
if (left === "String" || right === "String") {
|
|
8170
|
-
// Boolean + String is an error, and
|
|
8171
|
-
// Float/Double + String is legal, but its hard to predict
|
|
8172
|
-
// the way the float will be formatted (and it won't match
|
|
8173
|
-
// what javascript would do by default)
|
|
8174
|
-
if (/Float|Double|Boolean/.test(left + right)) {
|
|
8175
|
-
return null;
|
|
8176
|
-
}
|
|
8177
|
-
return ["String", String];
|
|
8178
|
-
}
|
|
8179
|
-
if (left === "Char" || right === "Char") {
|
|
8180
|
-
if (left === right) {
|
|
8181
|
-
// adding two chars produces a string
|
|
8182
|
-
return ["String", String];
|
|
8183
|
-
}
|
|
8184
|
-
if (/Number|Long/.test(left + right)) {
|
|
8185
|
-
return ["Char", (v) => v];
|
|
8186
|
-
}
|
|
8187
|
-
}
|
|
8188
|
-
return common_arith_types(left, right);
|
|
8189
|
-
}
|
|
8190
|
-
function shift_mod_types(left, right) {
|
|
8191
|
-
const result = common_bitwise_types(left, right);
|
|
8192
|
-
if (result && result[0] === "Boolean") {
|
|
10983
|
+
if (istate.state.inType) {
|
|
8193
10984
|
return null;
|
|
8194
10985
|
}
|
|
8195
|
-
return result;
|
|
8196
|
-
}
|
|
8197
|
-
function equalsFn(left, right) {
|
|
8198
|
-
const lt = typeof left;
|
|
8199
|
-
const rt = typeof right;
|
|
8200
|
-
return lt === "string" || rt === "string"
|
|
8201
|
-
? // two string literals will compare unequal, becuase string
|
|
8202
|
-
// equality is object equality.
|
|
8203
|
-
false
|
|
8204
|
-
: (lt === "number" || lt === "bigint") &&
|
|
8205
|
-
(rt === "number" || rt === "bigint")
|
|
8206
|
-
? // numeric types are compared for value equality
|
|
8207
|
-
left == right
|
|
8208
|
-
: // otherwise types and values must match
|
|
8209
|
-
left === right;
|
|
8210
|
-
}
|
|
8211
|
-
const operators = {
|
|
8212
|
-
"+": {
|
|
8213
|
-
typeFn: plus_types,
|
|
8214
|
-
valueFn: (left, right) => typeof left === "string" && typeof right !== "string"
|
|
8215
|
-
? String.fromCharCode(left.charCodeAt(0) + Number(right))
|
|
8216
|
-
: typeof left !== "string" && typeof right === "string"
|
|
8217
|
-
? String.fromCharCode(right.charCodeAt(0) + Number(left))
|
|
8218
|
-
: left + right,
|
|
8219
|
-
},
|
|
8220
|
-
"-": {
|
|
8221
|
-
typeFn: common_arith_types,
|
|
8222
|
-
valueFn: (left, right) => left - right,
|
|
8223
|
-
},
|
|
8224
|
-
"*": {
|
|
8225
|
-
typeFn: common_arith_types,
|
|
8226
|
-
valueFn: (left, right) => left * right,
|
|
8227
|
-
},
|
|
8228
|
-
"/": {
|
|
8229
|
-
typeFn: common_arith_types,
|
|
8230
|
-
valueFn: (left, right) => left / right,
|
|
8231
|
-
},
|
|
8232
|
-
"%": {
|
|
8233
|
-
typeFn: shift_mod_types,
|
|
8234
|
-
valueFn: (left, right) => left % right,
|
|
8235
|
-
},
|
|
8236
|
-
"&": {
|
|
8237
|
-
typeFn: common_bitwise_types,
|
|
8238
|
-
valueFn: (left, right) => left & right,
|
|
8239
|
-
},
|
|
8240
|
-
"|": {
|
|
8241
|
-
typeFn: common_bitwise_types,
|
|
8242
|
-
valueFn: (left, right) => left | right,
|
|
8243
|
-
},
|
|
8244
|
-
"^": {
|
|
8245
|
-
typeFn: common_bitwise_types,
|
|
8246
|
-
valueFn: (left, right) => left ^ right,
|
|
8247
|
-
},
|
|
8248
|
-
"<<": {
|
|
8249
|
-
typeFn: shift_mod_types,
|
|
8250
|
-
valueFn: (left, right) => typeof right === "bigint"
|
|
8251
|
-
? left << right
|
|
8252
|
-
: left << right,
|
|
8253
|
-
},
|
|
8254
|
-
">>": {
|
|
8255
|
-
typeFn: shift_mod_types,
|
|
8256
|
-
valueFn: (left, right) => typeof right === "bigint"
|
|
8257
|
-
? left >> right
|
|
8258
|
-
: left >> right,
|
|
8259
|
-
},
|
|
8260
|
-
"==": {
|
|
8261
|
-
typeFn: () => ["Boolean", (v) => v],
|
|
8262
|
-
valueFn: equalsFn,
|
|
8263
|
-
},
|
|
8264
|
-
"!=": {
|
|
8265
|
-
typeFn: () => ["Boolean", (v) => v],
|
|
8266
|
-
valueFn: (left, right) => !equalsFn(left, right),
|
|
8267
|
-
},
|
|
8268
|
-
"<=": {
|
|
8269
|
-
typeFn: common_arith_types,
|
|
8270
|
-
valueFn: (left, right) => left <= right,
|
|
8271
|
-
},
|
|
8272
|
-
">=": {
|
|
8273
|
-
typeFn: common_arith_types,
|
|
8274
|
-
valueFn: (left, right) => left >= right,
|
|
8275
|
-
},
|
|
8276
|
-
"<": {
|
|
8277
|
-
typeFn: common_arith_types,
|
|
8278
|
-
valueFn: (left, right) => left < right,
|
|
8279
|
-
},
|
|
8280
|
-
">": {
|
|
8281
|
-
typeFn: common_arith_types,
|
|
8282
|
-
valueFn: (left, right) => left > right,
|
|
8283
|
-
},
|
|
8284
|
-
as: null,
|
|
8285
|
-
instanceof: null,
|
|
8286
|
-
has: null,
|
|
8287
|
-
};
|
|
8288
|
-
function optimizeNode(state, node) {
|
|
8289
10986
|
switch (node.type) {
|
|
8290
|
-
case "
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
break;
|
|
8294
|
-
switch (node.operator) {
|
|
8295
|
-
case "+":
|
|
8296
|
-
if (type === "Number" ||
|
|
8297
|
-
type === "Long" ||
|
|
8298
|
-
type === "Float" ||
|
|
8299
|
-
type === "Double" ||
|
|
8300
|
-
type === "Char" ||
|
|
8301
|
-
type === "String") {
|
|
8302
|
-
return arg;
|
|
8303
|
-
}
|
|
8304
|
-
break;
|
|
8305
|
-
case "-":
|
|
8306
|
-
if (type === "Number" ||
|
|
8307
|
-
type === "Long" ||
|
|
8308
|
-
type === "Float" ||
|
|
8309
|
-
type === "Double") {
|
|
8310
|
-
return replacementLiteral(node, -arg.value, type);
|
|
8311
|
-
}
|
|
8312
|
-
break;
|
|
8313
|
-
case "!":
|
|
8314
|
-
case "~":
|
|
8315
|
-
{
|
|
8316
|
-
if (type === "Number" || type === "Long") {
|
|
8317
|
-
return replacementLiteral(node, ~BigInt(arg.value), type);
|
|
8318
|
-
}
|
|
8319
|
-
if (type === "Boolean" && node.operator == "!") {
|
|
8320
|
-
return replacementLiteral(node, !arg.value, type);
|
|
8321
|
-
}
|
|
8322
|
-
}
|
|
8323
|
-
break;
|
|
8324
|
-
}
|
|
8325
|
-
break;
|
|
8326
|
-
}
|
|
8327
|
-
case "BinaryExpression": {
|
|
8328
|
-
const op = operators[node.operator];
|
|
8329
|
-
if (op) {
|
|
8330
|
-
const [left, left_type] = getNodeValue(node.left);
|
|
8331
|
-
const [right, right_type] = getNodeValue(node.right);
|
|
8332
|
-
if (!left || !right)
|
|
8333
|
-
break;
|
|
8334
|
-
const type = op.typeFn(left_type, right_type);
|
|
8335
|
-
if (!type)
|
|
8336
|
-
break;
|
|
8337
|
-
const value = op.valueFn(type[1](left.value), type[1](right.value));
|
|
8338
|
-
if (value === null)
|
|
8339
|
-
break;
|
|
8340
|
-
return replacementLiteral(node, value, type[0]);
|
|
8341
|
-
}
|
|
10987
|
+
case "UpdateExpression":
|
|
10988
|
+
// we only evaluated any subexpressions of the argument.
|
|
10989
|
+
evaluateNode(istate, node.argument);
|
|
8342
10990
|
break;
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
const
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
const falsy = left.value === false ||
|
|
8349
|
-
left.value === null ||
|
|
8350
|
-
((left_type === "Number" || left_type === "Long") &&
|
|
8351
|
-
(left.value === 0 || left.value === 0n));
|
|
8352
|
-
if (falsy === (node.operator === "&&" || node.operator === "and")) {
|
|
8353
|
-
return left;
|
|
8354
|
-
}
|
|
8355
|
-
if (left_type !== "Boolean" &&
|
|
8356
|
-
left_type !== "Number" &&
|
|
8357
|
-
left_type !== "Long") {
|
|
8358
|
-
break;
|
|
8359
|
-
}
|
|
8360
|
-
const [right, right_type] = getNodeValue(node.right);
|
|
8361
|
-
if (right && right_type === left_type) {
|
|
8362
|
-
if (left_type === "Boolean" ||
|
|
8363
|
-
node.operator === "||" ||
|
|
8364
|
-
node.operator === "or") {
|
|
8365
|
-
return right;
|
|
8366
|
-
}
|
|
8367
|
-
if (node.operator !== "&&" && node.operator !== "and") {
|
|
8368
|
-
throw new Error(`Unexpected operator "${node.operator}"`);
|
|
8369
|
-
}
|
|
8370
|
-
return { ...node, type: "BinaryExpression", operator: "&" };
|
|
8371
|
-
}
|
|
8372
|
-
if (left_type === "Boolean") {
|
|
8373
|
-
if (isBooleanExpression(state, node.right)) {
|
|
8374
|
-
return node.right;
|
|
8375
|
-
}
|
|
8376
|
-
}
|
|
10991
|
+
case "AssignmentExpression": {
|
|
10992
|
+
// we only evaluated any subexpressions of the lhs.
|
|
10993
|
+
const right = istate.stack.pop();
|
|
10994
|
+
evaluateNode(istate, node.left);
|
|
10995
|
+
istate.stack.push(right);
|
|
8377
10996
|
break;
|
|
8378
10997
|
}
|
|
8379
|
-
case "
|
|
8380
|
-
if (node.
|
|
8381
|
-
node.
|
|
10998
|
+
case "BinaryExpression":
|
|
10999
|
+
if (node.operator === "has" &&
|
|
11000
|
+
node.right.type === "UnaryExpression" &&
|
|
11001
|
+
node.right.operator === ":") {
|
|
11002
|
+
// we skipped this node, so evaluate it now...
|
|
11003
|
+
istate.stack.push(interp_evaluate(istate, node.right));
|
|
8382
11004
|
}
|
|
8383
11005
|
break;
|
|
8384
11006
|
}
|
|
8385
|
-
|
|
11007
|
+
const before = beforeEvaluate(istate, node);
|
|
11008
|
+
if (before != null)
|
|
11009
|
+
return before;
|
|
11010
|
+
evaluateNode(istate, node);
|
|
11011
|
+
return afterEvaluate(istate, node);
|
|
8386
11012
|
}
|
|
8387
|
-
function evaluateFunction(
|
|
8388
|
-
if (!func.body ||
|
|
11013
|
+
function evaluateFunction(istate, func, args) {
|
|
11014
|
+
if (!func.body ||
|
|
11015
|
+
istate.state.inlining ||
|
|
11016
|
+
(args && args.length != func.params.length)) {
|
|
8389
11017
|
return false;
|
|
8390
11018
|
}
|
|
8391
11019
|
const paramValues = args &&
|
|
8392
|
-
Object.fromEntries(func.params.map((p, i) => [
|
|
11020
|
+
Object.fromEntries(func.params.map((p, i) => [
|
|
11021
|
+
(0,external_api_cjs_namespaceObject.variableDeclarationName)(p),
|
|
11022
|
+
args[i],
|
|
11023
|
+
]));
|
|
8393
11024
|
let ret = null;
|
|
8394
11025
|
const body = args ? cloneDeep(func.body) : func.body;
|
|
11026
|
+
const depth = istate.stack.length;
|
|
8395
11027
|
try {
|
|
8396
11028
|
traverseAst(body, (node) => {
|
|
8397
11029
|
switch (node.type) {
|
|
@@ -8413,24 +11045,28 @@ function evaluateFunction(state, func, args) {
|
|
|
8413
11045
|
ret = node.argument || null;
|
|
8414
11046
|
return null;
|
|
8415
11047
|
case "BlockStatement":
|
|
8416
|
-
case "Literal":
|
|
8417
11048
|
return null;
|
|
8418
11049
|
case "Identifier":
|
|
8419
11050
|
if ((0,external_api_cjs_namespaceObject.hasProperty)(paramValues, node.name)) {
|
|
8420
|
-
|
|
11051
|
+
istate.stack.push(interp_evaluate(istate, (node = paramValues[node.name])));
|
|
11052
|
+
return node;
|
|
8421
11053
|
}
|
|
8422
11054
|
// fall through;
|
|
8423
11055
|
default: {
|
|
8424
|
-
const repl = optimizeNode(
|
|
8425
|
-
if (repl
|
|
11056
|
+
const repl = optimizeNode(istate, node) || node;
|
|
11057
|
+
if (repl.type === "Literal")
|
|
8426
11058
|
return repl;
|
|
8427
11059
|
throw new Error("Didn't optimize");
|
|
8428
11060
|
}
|
|
8429
11061
|
}
|
|
8430
11062
|
});
|
|
11063
|
+
delete istate.state.inlining;
|
|
11064
|
+
istate.stack.length = depth;
|
|
8431
11065
|
return ret;
|
|
8432
11066
|
}
|
|
8433
11067
|
catch (e) {
|
|
11068
|
+
delete istate.state.inlining;
|
|
11069
|
+
istate.stack.length = depth;
|
|
8434
11070
|
return false;
|
|
8435
11071
|
}
|
|
8436
11072
|
}
|
|
@@ -8449,51 +11085,22 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8449
11085
|
const replace = (node, old) => {
|
|
8450
11086
|
if (node === false || node === null)
|
|
8451
11087
|
return node;
|
|
11088
|
+
if (isExpression(node)) {
|
|
11089
|
+
popIstate(istate, old);
|
|
11090
|
+
}
|
|
8452
11091
|
const rep = state.traverse(node);
|
|
8453
11092
|
if (rep === false || Array.isArray(rep))
|
|
8454
11093
|
return rep;
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
obj = {
|
|
8464
|
-
type: "BinaryExpression",
|
|
8465
|
-
operator: "as",
|
|
8466
|
-
left: { ...obj, start, end, loc },
|
|
8467
|
-
right: { type: "TypeSpecList", ts: [obj.enumType] },
|
|
8468
|
-
};
|
|
8469
|
-
}
|
|
8470
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
8471
|
-
node[k] = v;
|
|
8472
|
-
}
|
|
8473
|
-
node.loc = loc;
|
|
8474
|
-
node.start = start;
|
|
8475
|
-
node.end = end;
|
|
8476
|
-
};
|
|
8477
|
-
const lookupAndReplace = (node) => {
|
|
8478
|
-
const [, objects] = state.lookup(node);
|
|
8479
|
-
if (!objects) {
|
|
8480
|
-
return false;
|
|
8481
|
-
}
|
|
8482
|
-
let obj = getLiteralFromDecls(objects);
|
|
8483
|
-
if (!obj) {
|
|
8484
|
-
return false;
|
|
8485
|
-
}
|
|
8486
|
-
while (obj.type === "BinaryExpression") {
|
|
8487
|
-
if (obj.left.type === "BinaryExpression" && obj.left.operator === "as") {
|
|
8488
|
-
obj = { ...obj, left: obj.left.left };
|
|
8489
|
-
}
|
|
8490
|
-
else {
|
|
8491
|
-
obj = { ...obj, left: { ...obj.left } };
|
|
8492
|
-
break;
|
|
8493
|
-
}
|
|
11094
|
+
const result = {
|
|
11095
|
+
...(rep || node),
|
|
11096
|
+
loc: old.loc,
|
|
11097
|
+
start: old.start,
|
|
11098
|
+
end: old.end,
|
|
11099
|
+
};
|
|
11100
|
+
if (isExpression(result)) {
|
|
11101
|
+
istate.stack[istate.stack.length - 1].node = result;
|
|
8494
11102
|
}
|
|
8495
|
-
|
|
8496
|
-
return true;
|
|
11103
|
+
return result;
|
|
8497
11104
|
};
|
|
8498
11105
|
const topLocals = () => state.localsStack[state.localsStack.length - 1];
|
|
8499
11106
|
/*
|
|
@@ -8586,48 +11193,14 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8586
11193
|
}
|
|
8587
11194
|
}
|
|
8588
11195
|
};
|
|
11196
|
+
// use this when optimizing initializer expressions,
|
|
11197
|
+
// outside of any function.
|
|
11198
|
+
const gistate = { state, stack: [] };
|
|
11199
|
+
// use this when type inference is enabled, and we're
|
|
11200
|
+
// inside a function.
|
|
11201
|
+
let istate = gistate;
|
|
8589
11202
|
state.pre = (node) => {
|
|
8590
11203
|
switch (node.type) {
|
|
8591
|
-
case "ConditionalExpression":
|
|
8592
|
-
case "IfStatement":
|
|
8593
|
-
case "DoWhileStatement":
|
|
8594
|
-
case "WhileStatement": {
|
|
8595
|
-
const test = (state.traverse(node.test) ||
|
|
8596
|
-
node.test);
|
|
8597
|
-
const [value, type] = getNodeValue(test);
|
|
8598
|
-
if (value) {
|
|
8599
|
-
let result = null;
|
|
8600
|
-
if (type === "Null") {
|
|
8601
|
-
result = false;
|
|
8602
|
-
}
|
|
8603
|
-
else if (type === "Boolean" ||
|
|
8604
|
-
type === "Number" ||
|
|
8605
|
-
type === "Long") {
|
|
8606
|
-
result = !!value.value;
|
|
8607
|
-
}
|
|
8608
|
-
if (result !== null) {
|
|
8609
|
-
node.test = {
|
|
8610
|
-
type: "Literal",
|
|
8611
|
-
value: result,
|
|
8612
|
-
raw: result.toString(),
|
|
8613
|
-
};
|
|
8614
|
-
if (node.type === "IfStatement" ||
|
|
8615
|
-
node.type === "ConditionalExpression") {
|
|
8616
|
-
return [result ? "consequent" : "alternate"];
|
|
8617
|
-
}
|
|
8618
|
-
else if (node.type === "WhileStatement") {
|
|
8619
|
-
return result === false ? [] : ["body"];
|
|
8620
|
-
}
|
|
8621
|
-
else if (node.type === "DoWhileStatement") {
|
|
8622
|
-
return ["body"];
|
|
8623
|
-
}
|
|
8624
|
-
else {
|
|
8625
|
-
throw new Error("Unexpected Node type");
|
|
8626
|
-
}
|
|
8627
|
-
}
|
|
8628
|
-
}
|
|
8629
|
-
return null;
|
|
8630
|
-
}
|
|
8631
11204
|
case "EnumDeclaration":
|
|
8632
11205
|
return [];
|
|
8633
11206
|
case "ForStatement": {
|
|
@@ -8649,13 +11222,14 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8649
11222
|
}
|
|
8650
11223
|
break;
|
|
8651
11224
|
case "BinaryExpression":
|
|
8652
|
-
if (node.operator === "has"
|
|
8653
|
-
|
|
8654
|
-
node.right.operator === ":"
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
11225
|
+
if (node.operator === "has"
|
|
11226
|
+
? node.right.type === "UnaryExpression" &&
|
|
11227
|
+
node.right.operator === ":"
|
|
11228
|
+
: node.operator === "as") {
|
|
11229
|
+
// Using `expr has :symbol` doesn't "expose"
|
|
11230
|
+
// symbol, and the rhs of an "as" isn't an
|
|
11231
|
+
// expression. In both cases, skip the rhs
|
|
11232
|
+
return ["left"];
|
|
8659
11233
|
}
|
|
8660
11234
|
break;
|
|
8661
11235
|
case "UnaryExpression":
|
|
@@ -8695,29 +11269,8 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8695
11269
|
}
|
|
8696
11270
|
}
|
|
8697
11271
|
}
|
|
8698
|
-
if ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, node.name)) {
|
|
8699
|
-
if (!lookupAndReplace(node)) {
|
|
8700
|
-
state.usedByName[node.name] = true;
|
|
8701
|
-
}
|
|
8702
|
-
}
|
|
8703
11272
|
return [];
|
|
8704
11273
|
}
|
|
8705
|
-
case "MemberExpression": {
|
|
8706
|
-
const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
|
|
8707
|
-
if (property) {
|
|
8708
|
-
if ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, property.name)) {
|
|
8709
|
-
if (lookupAndReplace(node)) {
|
|
8710
|
-
return false;
|
|
8711
|
-
}
|
|
8712
|
-
else {
|
|
8713
|
-
state.usedByName[property.name] = true;
|
|
8714
|
-
}
|
|
8715
|
-
}
|
|
8716
|
-
// Don't optimize the property.
|
|
8717
|
-
return ["object"];
|
|
8718
|
-
}
|
|
8719
|
-
break;
|
|
8720
|
-
}
|
|
8721
11274
|
case "AssignmentExpression":
|
|
8722
11275
|
case "UpdateExpression": {
|
|
8723
11276
|
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
@@ -8733,9 +11286,15 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8733
11286
|
}
|
|
8734
11287
|
}
|
|
8735
11288
|
else if (lhs.type === "MemberExpression") {
|
|
8736
|
-
state.traverse(lhs.object);
|
|
8737
|
-
if (
|
|
8738
|
-
|
|
11289
|
+
const object = state.traverse(lhs.object);
|
|
11290
|
+
if (object) {
|
|
11291
|
+
lhs.object = object;
|
|
11292
|
+
}
|
|
11293
|
+
if (!(0,external_api_cjs_namespaceObject.isLookupCandidate)(lhs)) {
|
|
11294
|
+
const property = state.traverse(lhs.property);
|
|
11295
|
+
if (property) {
|
|
11296
|
+
lhs.property = property;
|
|
11297
|
+
}
|
|
8739
11298
|
}
|
|
8740
11299
|
}
|
|
8741
11300
|
return node.type === "AssignmentExpression" ? ["right"] : [];
|
|
@@ -8760,6 +11319,23 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8760
11319
|
throw new Error(`Nested functions: ${self.fullName} was activated during processing of ${state.currentFunction.fullName}`);
|
|
8761
11320
|
}
|
|
8762
11321
|
state.currentFunction = self;
|
|
11322
|
+
const is = !state.config?.propagateTypes ||
|
|
11323
|
+
node.attrs?.attributes?.elements.find((attr) => attr.type == "UnaryExpression" &&
|
|
11324
|
+
attr.argument.name === "noConstProp")
|
|
11325
|
+
? null
|
|
11326
|
+
: type_flow_buildTypeInfo(state, state.currentFunction);
|
|
11327
|
+
if (is) {
|
|
11328
|
+
/*
|
|
11329
|
+
* istate contains a copy of state, but we need the real
|
|
11330
|
+
* thing, because "state" is captured here.
|
|
11331
|
+
*
|
|
11332
|
+
* A better solution will be to separate out a
|
|
11333
|
+
* "lookup context", which will be a stack, plus a couple
|
|
11334
|
+
* of fields from state, and then pass that around.
|
|
11335
|
+
*/
|
|
11336
|
+
is.state = state;
|
|
11337
|
+
istate = is;
|
|
11338
|
+
}
|
|
8763
11339
|
if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
|
|
8764
11340
|
let used = false;
|
|
8765
11341
|
if (node.id.name == "initialize") {
|
|
@@ -8772,7 +11348,15 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8772
11348
|
markFunctionCalled(state, node);
|
|
8773
11349
|
}
|
|
8774
11350
|
}
|
|
11351
|
+
// We dont want to call evaluateNode on
|
|
11352
|
+
// id, args or returnType
|
|
11353
|
+
return ["body"];
|
|
8775
11354
|
}
|
|
11355
|
+
case "ClassDeclaration":
|
|
11356
|
+
case "ModuleDeclaration":
|
|
11357
|
+
// We dont want to call evaluateNode on
|
|
11358
|
+
// id, or superClass
|
|
11359
|
+
return ["body"];
|
|
8776
11360
|
}
|
|
8777
11361
|
return null;
|
|
8778
11362
|
};
|
|
@@ -8781,18 +11365,25 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8781
11365
|
if (locals.node === node) {
|
|
8782
11366
|
state.localsStack.pop();
|
|
8783
11367
|
}
|
|
8784
|
-
const opt = optimizeNode(
|
|
8785
|
-
if (opt) {
|
|
8786
|
-
return
|
|
11368
|
+
const opt = optimizeNode(istate, node);
|
|
11369
|
+
if (opt != null) {
|
|
11370
|
+
return opt;
|
|
8787
11371
|
}
|
|
8788
11372
|
switch (node.type) {
|
|
8789
11373
|
case "FunctionDeclaration":
|
|
11374
|
+
if (node.body && evaluateFunction(istate, node, null) !== false) {
|
|
11375
|
+
node.optimizable = true;
|
|
11376
|
+
}
|
|
8790
11377
|
if (!state.currentFunction) {
|
|
8791
11378
|
throw new Error(`Finished function ${state.stack.slice(-1)[0].fullName}, but it was not marked current`);
|
|
8792
11379
|
}
|
|
8793
11380
|
state.currentFunction.info = state.currentFunction.next_info || false;
|
|
8794
11381
|
delete state.currentFunction.next_info;
|
|
8795
11382
|
delete state.currentFunction;
|
|
11383
|
+
if (istate.stack.length) {
|
|
11384
|
+
throw new Error("Stack was not empty");
|
|
11385
|
+
}
|
|
11386
|
+
istate = gistate;
|
|
8796
11387
|
break;
|
|
8797
11388
|
case "BlockStatement":
|
|
8798
11389
|
if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
|
|
@@ -8804,74 +11395,32 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8804
11395
|
cleanupUnusedVars(state, node);
|
|
8805
11396
|
}
|
|
8806
11397
|
break;
|
|
8807
|
-
case "
|
|
8808
|
-
|
|
8809
|
-
if (
|
|
8810
|
-
|
|
8811
|
-
const rep = node.test.value ? node.consequent : node.alternate;
|
|
8812
|
-
if (!rep)
|
|
8813
|
-
return false;
|
|
8814
|
-
return replace(rep, rep);
|
|
8815
|
-
}
|
|
8816
|
-
else if (node.type === "IfStatement") {
|
|
8817
|
-
if (node.alternate &&
|
|
8818
|
-
node.alternate.type === "BlockStatement" &&
|
|
8819
|
-
!node.alternate.body.length) {
|
|
8820
|
-
delete node.alternate;
|
|
8821
|
-
}
|
|
8822
|
-
else {
|
|
8823
|
-
const call = inlinableSubExpression(node.test);
|
|
8824
|
-
if (call) {
|
|
8825
|
-
return replace(optimizeCall(state, call, node), node.test);
|
|
8826
|
-
}
|
|
8827
|
-
}
|
|
8828
|
-
}
|
|
8829
|
-
break;
|
|
8830
|
-
case "WhileStatement":
|
|
8831
|
-
if (node.test.type === "Literal" && node.test.value === false) {
|
|
8832
|
-
return false;
|
|
8833
|
-
}
|
|
8834
|
-
break;
|
|
8835
|
-
case "DoWhileStatement":
|
|
8836
|
-
if (node.test.type === "Literal" && node.test.value === false) {
|
|
8837
|
-
return node.body;
|
|
11398
|
+
case "IfStatement": {
|
|
11399
|
+
const call = inlinableSubExpression(node.test);
|
|
11400
|
+
if (call) {
|
|
11401
|
+
return replace(optimizeCall(istate, call, node), node.test);
|
|
8838
11402
|
}
|
|
8839
11403
|
break;
|
|
11404
|
+
}
|
|
8840
11405
|
case "ReturnStatement":
|
|
8841
11406
|
if (node.argument && node.argument.type === "CallExpression") {
|
|
8842
|
-
return replace(optimizeCall(
|
|
11407
|
+
return replace(optimizeCall(istate, node.argument, node), node.argument);
|
|
8843
11408
|
}
|
|
8844
11409
|
break;
|
|
8845
|
-
case "
|
|
8846
|
-
if (node.
|
|
8847
|
-
node.
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
obj.type === "Program" ||
|
|
8856
|
-
obj.type === "ClassDeclaration") &&
|
|
8857
|
-
obj.stack) {
|
|
8858
|
-
const exists = (0,external_api_cjs_namespaceObject.hasProperty)(obj.decls, node.right.argument.name) ||
|
|
8859
|
-
// This is overkill, since we've already looked up
|
|
8860
|
-
// node.left, but the actual lookup rules are complicated,
|
|
8861
|
-
// and embedded within state.lookup; so just defer to that.
|
|
8862
|
-
state.lookup({
|
|
8863
|
-
type: "MemberExpression",
|
|
8864
|
-
object: node.left,
|
|
8865
|
-
property: node.right.argument,
|
|
8866
|
-
computed: false,
|
|
8867
|
-
})[1];
|
|
8868
|
-
if (!exists) {
|
|
8869
|
-
return replace({ type: "Literal", value: false, raw: "false" }, node);
|
|
8870
|
-
}
|
|
8871
|
-
}
|
|
11410
|
+
case "Identifier":
|
|
11411
|
+
if ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, node.name)) {
|
|
11412
|
+
state.usedByName[node.name] = true;
|
|
11413
|
+
}
|
|
11414
|
+
break;
|
|
11415
|
+
case "MemberExpression": {
|
|
11416
|
+
const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
|
|
11417
|
+
if (property) {
|
|
11418
|
+
if ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, property.name)) {
|
|
11419
|
+
state.usedByName[property.name] = true;
|
|
8872
11420
|
}
|
|
8873
11421
|
}
|
|
8874
11422
|
break;
|
|
11423
|
+
}
|
|
8875
11424
|
case "NewExpression":
|
|
8876
11425
|
if (state.currentFunction) {
|
|
8877
11426
|
const [, results] = state.lookup(node.callee);
|
|
@@ -8884,7 +11433,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8884
11433
|
}
|
|
8885
11434
|
break;
|
|
8886
11435
|
case "CallExpression": {
|
|
8887
|
-
return replace(optimizeCall(
|
|
11436
|
+
return replace(optimizeCall(istate, node, null), node);
|
|
8888
11437
|
}
|
|
8889
11438
|
case "VariableDeclaration": {
|
|
8890
11439
|
const locals = topLocals();
|
|
@@ -8901,7 +11450,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8901
11450
|
continue;
|
|
8902
11451
|
const call = inlinableSubExpression(decl.init);
|
|
8903
11452
|
if (call) {
|
|
8904
|
-
const inlined = replace(optimizeCall(
|
|
11453
|
+
const inlined = replace(optimizeCall(istate, call, decl), decl.init);
|
|
8905
11454
|
if (!inlined)
|
|
8906
11455
|
continue;
|
|
8907
11456
|
if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
|
|
@@ -8933,7 +11482,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8933
11482
|
}
|
|
8934
11483
|
case "ExpressionStatement":
|
|
8935
11484
|
if (node.expression.type === "CallExpression") {
|
|
8936
|
-
return replace(optimizeCall(
|
|
11485
|
+
return replace(optimizeCall(istate, node.expression, node), node.expression);
|
|
8937
11486
|
}
|
|
8938
11487
|
else if (node.expression.type === "AssignmentExpression") {
|
|
8939
11488
|
const call = inlinableSubExpression(node.expression.right);
|
|
@@ -8949,7 +11498,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8949
11498
|
ok = !!result;
|
|
8950
11499
|
}
|
|
8951
11500
|
if (ok) {
|
|
8952
|
-
return replace(optimizeCall(
|
|
11501
|
+
return replace(optimizeCall(istate, call, node.expression), node.expression.right);
|
|
8953
11502
|
}
|
|
8954
11503
|
}
|
|
8955
11504
|
}
|
|
@@ -8968,7 +11517,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
8968
11517
|
node.left.type === "Identifier" &&
|
|
8969
11518
|
node.right.type === "Identifier" &&
|
|
8970
11519
|
node.left.name === node.right.name) {
|
|
8971
|
-
return { type: "Literal", value: null, raw: "null" };
|
|
11520
|
+
return replace({ type: "Literal", value: null, raw: "null" }, node);
|
|
8972
11521
|
}
|
|
8973
11522
|
// fall through;
|
|
8974
11523
|
case "UpdateExpression":
|
|
@@ -9116,7 +11665,8 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
9116
11665
|
});
|
|
9117
11666
|
return state.diagnostics;
|
|
9118
11667
|
}
|
|
9119
|
-
function optimizeCall(
|
|
11668
|
+
function optimizeCall(istate, node, context) {
|
|
11669
|
+
const state = istate.state;
|
|
9120
11670
|
const [name, results] = state.lookupNonlocal(node.callee);
|
|
9121
11671
|
const callees = results ? findCallees(results) : null;
|
|
9122
11672
|
if (!callees || !callees.length) {
|
|
@@ -9151,7 +11701,7 @@ function optimizeCall(state, node, context) {
|
|
|
9151
11701
|
callee.optimizable &&
|
|
9152
11702
|
!callee.hasOverride &&
|
|
9153
11703
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
9154
|
-
const ret = evaluateFunction(
|
|
11704
|
+
const ret = evaluateFunction(istate, callee, node.arguments);
|
|
9155
11705
|
if (ret) {
|
|
9156
11706
|
return ret;
|
|
9157
11707
|
}
|
|
@@ -9620,7 +12170,10 @@ const configOptionsToCheck = [
|
|
|
9620
12170
|
"ignoredSourcePaths",
|
|
9621
12171
|
"checkInvalidSymbols",
|
|
9622
12172
|
"checkCompilerLookupRules",
|
|
12173
|
+
"checkTypes",
|
|
9623
12174
|
"sizeBasedPRE",
|
|
12175
|
+
"trustDeclaredTypes",
|
|
12176
|
+
"propagateTypes",
|
|
9624
12177
|
"extensionVersion",
|
|
9625
12178
|
];
|
|
9626
12179
|
/**
|
|
@@ -9680,7 +12233,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
|
|
|
9680
12233
|
// the oldest optimized file, we don't need to regenerate
|
|
9681
12234
|
const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
|
|
9682
12235
|
const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
|
|
9683
|
-
if (source_time < opt_time &&
|
|
12236
|
+
if (source_time < opt_time && 1673220499844 < opt_time) {
|
|
9684
12237
|
return { hasTests, diagnostics: prevDiagnostics };
|
|
9685
12238
|
}
|
|
9686
12239
|
}
|
|
@@ -9707,7 +12260,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
|
|
|
9707
12260
|
return promises_namespaceObject.writeFile(external_path_.join(output, "build-info.json"), JSON.stringify({
|
|
9708
12261
|
hasTests,
|
|
9709
12262
|
diagnostics,
|
|
9710
|
-
optimizerVersion: "1.0
|
|
12263
|
+
optimizerVersion: "1.1.0",
|
|
9711
12264
|
...Object.fromEntries(configOptionsToCheck.map((option) => [option, config[option]])),
|
|
9712
12265
|
}))
|
|
9713
12266
|
.then(() => ({ hasTests, diagnostics }));
|