@markw65/monkeyc-optimizer 1.1.10 → 1.1.12
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 +25 -0
- package/build/api.cjs +958 -317
- package/build/optimizer.cjs +4407 -3771
- package/build/src/api.d.ts +1 -1
- package/build/src/control-flow.d.ts +1 -1
- package/build/src/data-flow.d.ts +2 -1
- package/build/src/inliner.d.ts +1 -0
- package/build/src/optimizer-types.d.ts +2 -0
- package/build/src/type-flow/dead-store.d.ts +10 -2
- package/build/src/type-flow/interp-call.d.ts +1 -1
- package/build/src/type-flow/type-flow-util.d.ts +1 -0
- package/build/src/type-flow/types.d.ts +1 -1
- package/build/src/type-flow.d.ts +2 -2
- package/package.json +2 -2
package/build/api.cjs
CHANGED
|
@@ -619,7 +619,7 @@ class LocalState {
|
|
|
619
619
|
this.unreachable = true;
|
|
620
620
|
}
|
|
621
621
|
}
|
|
622
|
-
function buildReducedGraph(state, func, notice) {
|
|
622
|
+
function buildReducedGraph(state, func, refsForUpdate, notice) {
|
|
623
623
|
const { stack, pre, post } = state;
|
|
624
624
|
try {
|
|
625
625
|
const localState = new LocalState(func.node);
|
|
@@ -627,8 +627,11 @@ function buildReducedGraph(state, func, notice) {
|
|
|
627
627
|
state.stack = [...func.stack];
|
|
628
628
|
const stmtStack = [func.node];
|
|
629
629
|
const testStack = [{ node: func.node }];
|
|
630
|
+
const allEvents = [];
|
|
631
|
+
const eventsStack = [];
|
|
630
632
|
let tryActive = 0;
|
|
631
633
|
state.pre = (node) => {
|
|
634
|
+
eventsStack.push(allEvents.length);
|
|
632
635
|
if (state.inType || localState.unreachable) {
|
|
633
636
|
return [];
|
|
634
637
|
}
|
|
@@ -954,6 +957,12 @@ function buildReducedGraph(state, func, notice) {
|
|
|
954
957
|
}
|
|
955
958
|
return [];
|
|
956
959
|
case "AssignmentExpression":
|
|
960
|
+
if (refsForUpdate && node.operator !== "=") {
|
|
961
|
+
// if its an update, we need to see a "ref"
|
|
962
|
+
// of the lhs, then whatever happens on the rhs,
|
|
963
|
+
// and then the assignment itself
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
957
966
|
if (node.left.type === "MemberExpression") {
|
|
958
967
|
state.traverse(node.left.object);
|
|
959
968
|
if (node.left.computed) {
|
|
@@ -983,6 +992,7 @@ function buildReducedGraph(state, func, notice) {
|
|
|
983
992
|
return null;
|
|
984
993
|
};
|
|
985
994
|
const addEvent = (block, event) => {
|
|
995
|
+
allEvents.push(event);
|
|
986
996
|
if (!block.events) {
|
|
987
997
|
block.events = [event];
|
|
988
998
|
}
|
|
@@ -991,11 +1001,13 @@ function buildReducedGraph(state, func, notice) {
|
|
|
991
1001
|
}
|
|
992
1002
|
};
|
|
993
1003
|
state.post = (node) => {
|
|
1004
|
+
const eventIndex = eventsStack.pop();
|
|
1005
|
+
const getContainedEvents = () => allEvents.slice(eventIndex);
|
|
994
1006
|
const curStmt = stmtStack[stmtStack.length - 1];
|
|
995
1007
|
const topTest = testStack[testStack.length - 1];
|
|
996
1008
|
if (!state.inType) {
|
|
997
1009
|
const throws = tryActive > 0 && mayThrow(node);
|
|
998
|
-
const events = notice(node, curStmt, throws);
|
|
1010
|
+
const events = notice(node, curStmt, throws, getContainedEvents);
|
|
999
1011
|
if (throws) {
|
|
1000
1012
|
if (!events) {
|
|
1001
1013
|
throw new Error("mayThrow expression in try/catch must generate an event");
|
|
@@ -1042,7 +1054,7 @@ function buildReducedGraph(state, func, notice) {
|
|
|
1042
1054
|
throw new Error("Internal error: Unexpected successor edges");
|
|
1043
1055
|
}
|
|
1044
1056
|
localState.addEdge(localState.curBlock, topTest.false);
|
|
1045
|
-
const event = notice(node, curStmt, 1);
|
|
1057
|
+
const event = notice(node, curStmt, 1, getContainedEvents);
|
|
1046
1058
|
if (event) {
|
|
1047
1059
|
if (Array.isArray(event)) {
|
|
1048
1060
|
throw new Error(`Unexpected array of flw events`);
|
|
@@ -1183,8 +1195,10 @@ function getPreOrder(head) {
|
|
|
1183
1195
|
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6652);
|
|
1184
1196
|
/* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5101);
|
|
1185
1197
|
/* harmony import */ var _function_info__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(819);
|
|
1186
|
-
/* harmony import */ var
|
|
1187
|
-
/* harmony import */ var
|
|
1198
|
+
/* harmony import */ var _type_flow_type_flow_util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(1638);
|
|
1199
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(6906);
|
|
1200
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_6__);
|
|
1201
|
+
|
|
1188
1202
|
|
|
1189
1203
|
|
|
1190
1204
|
|
|
@@ -1349,7 +1363,7 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
|
|
|
1349
1363
|
};
|
|
1350
1364
|
return {
|
|
1351
1365
|
identifiers,
|
|
1352
|
-
graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
|
|
1366
|
+
graph: buildReducedGraph(state, func, wantsAllRefs, (node, stmt, mayThrow, getContainedEvents) => {
|
|
1353
1367
|
if (mayThrow === 1) {
|
|
1354
1368
|
return wantsAllRefs ? getFlowEvent(node, stmt, findDecl) : null;
|
|
1355
1369
|
}
|
|
@@ -1473,6 +1487,12 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
|
|
|
1473
1487
|
if (rhs)
|
|
1474
1488
|
def.rhs = rhs;
|
|
1475
1489
|
}
|
|
1490
|
+
if (declIsLocal(decl)) {
|
|
1491
|
+
const contained = getContainedEvents().filter((e) => e.type === "ref" || e.type === "mod");
|
|
1492
|
+
if (contained.length) {
|
|
1493
|
+
def.containedEvents = contained;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1476
1496
|
return def;
|
|
1477
1497
|
}
|
|
1478
1498
|
break;
|
|
@@ -1487,13 +1507,20 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
|
|
|
1487
1507
|
decl,
|
|
1488
1508
|
mayThrow,
|
|
1489
1509
|
};
|
|
1490
|
-
if (wantsAllRefs
|
|
1491
|
-
node.
|
|
1492
|
-
|
|
1493
|
-
node.
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1510
|
+
if (wantsAllRefs) {
|
|
1511
|
+
if ((node.right.type === "Identifier" ||
|
|
1512
|
+
node.right.type === "MemberExpression") &&
|
|
1513
|
+
node.operator === "=") {
|
|
1514
|
+
const rhs = findDecl(node.right);
|
|
1515
|
+
if (rhs)
|
|
1516
|
+
def.rhs = rhs;
|
|
1517
|
+
}
|
|
1518
|
+
if (declIsLocal(decl)) {
|
|
1519
|
+
const contained = getContainedEvents().filter((e) => e.type === "ref" || e.type === "mod");
|
|
1520
|
+
if (contained.length) {
|
|
1521
|
+
def.containedEvents = contained;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1497
1524
|
}
|
|
1498
1525
|
return def;
|
|
1499
1526
|
}
|
|
@@ -1546,6 +1573,12 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
|
|
|
1546
1573
|
mod.calleeObj = calleeDecl.base;
|
|
1547
1574
|
}
|
|
1548
1575
|
}
|
|
1576
|
+
else if (node.callee.type === "MemberExpression") {
|
|
1577
|
+
const calleeObj = findDecl(node.callee.object);
|
|
1578
|
+
if (calleeObj) {
|
|
1579
|
+
mod.calleeObj = calleeObj;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1549
1582
|
return mod;
|
|
1550
1583
|
}
|
|
1551
1584
|
}
|
|
@@ -1846,7 +1879,7 @@ function findCalleesByNode(state, callee) {
|
|
|
1846
1879
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
1847
1880
|
|
|
1848
1881
|
"use strict";
|
|
1849
|
-
/* unused harmony exports inlinableSubExpression, shouldInline, unused, inlineDiagnostic, inlineFunction, applyTypeIfNeeded */
|
|
1882
|
+
/* unused harmony exports inlinableSubExpression, inlineRequested, shouldInline, unused, inlineDiagnostic, inlineFunction, applyTypeIfNeeded */
|
|
1850
1883
|
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
|
|
1851
1884
|
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
|
|
1852
1885
|
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
|
|
@@ -2533,16 +2566,18 @@ function updateLocationForInline(node, original, context, func) {
|
|
|
2533
2566
|
throw new Error("Internal error: Inlined call had no location info");
|
|
2534
2567
|
}
|
|
2535
2568
|
traverseAst(node, (node) => {
|
|
2536
|
-
if (node.loc
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
if (
|
|
2541
|
-
node.origins
|
|
2569
|
+
if (!node.loc ||
|
|
2570
|
+
node.loc.source !== loc.source ||
|
|
2571
|
+
node.loc.start.offset > loc.end.offset ||
|
|
2572
|
+
node.loc.end.offset <= loc.start.offset) {
|
|
2573
|
+
if (node.loc) {
|
|
2574
|
+
if (!node.origins) {
|
|
2575
|
+
node.origins = [];
|
|
2576
|
+
}
|
|
2577
|
+
node.origins.unshift({ loc: node.loc, func: func.fullName });
|
|
2542
2578
|
}
|
|
2543
|
-
|
|
2579
|
+
withLoc(node, context, context === original ? context : false);
|
|
2544
2580
|
}
|
|
2545
|
-
withLoc(node, context, context === original ? context : false);
|
|
2546
2581
|
});
|
|
2547
2582
|
return node;
|
|
2548
2583
|
}
|
|
@@ -2570,8 +2605,8 @@ function inlineFunction(state, func, call, context) {
|
|
|
2570
2605
|
const ret = inlineFunctionHelper(state, func, call, context);
|
|
2571
2606
|
if (!ret)
|
|
2572
2607
|
return ret;
|
|
2573
|
-
const
|
|
2574
|
-
if (!
|
|
2608
|
+
const typecheckAttrs = func.node.attrs?.attributes?.elements.filter((attr) => isTypecheckArg(attr, null));
|
|
2609
|
+
if (!typecheckAttrs || !typecheckAttrs.length) {
|
|
2575
2610
|
return ret;
|
|
2576
2611
|
}
|
|
2577
2612
|
const callerElem = state.stack.find((elem) => elem.sn.type === "FunctionDeclaration");
|
|
@@ -2580,6 +2615,9 @@ function inlineFunction(state, func, call, context) {
|
|
|
2580
2615
|
}
|
|
2581
2616
|
const callerSn = callerElem.sn;
|
|
2582
2617
|
const caller = callerSn.node;
|
|
2618
|
+
if (caller.attrs?.attributes?.elements.find((attr) => isTypecheckArg(attr, null))) {
|
|
2619
|
+
return ret;
|
|
2620
|
+
}
|
|
2583
2621
|
if (!caller.attrs) {
|
|
2584
2622
|
caller.attrs = withLoc({
|
|
2585
2623
|
type: "AttributeList",
|
|
@@ -2588,10 +2626,7 @@ function inlineFunction(state, func, call, context) {
|
|
|
2588
2626
|
if (!caller.attrs.attributes) {
|
|
2589
2627
|
caller.attrs.attributes = withLoc({ type: "Attributes", elements: [] }, caller.attrs, false);
|
|
2590
2628
|
}
|
|
2591
|
-
|
|
2592
|
-
return ret;
|
|
2593
|
-
}
|
|
2594
|
-
caller.attrs.attributes.elements.unshift(withLocDeep({ ...typecheckFalse }, caller.attrs, false));
|
|
2629
|
+
caller.attrs.attributes.elements.unshift(...typecheckAttrs.map((typecheck) => withLocDeep({ ...typecheck }, caller.attrs, false)));
|
|
2595
2630
|
return ret;
|
|
2596
2631
|
}
|
|
2597
2632
|
function applyTypeIfNeeded(node) {
|
|
@@ -3177,6 +3212,8 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3177
3212
|
state.localsStack = [{}];
|
|
3178
3213
|
state.calledFunctions = {};
|
|
3179
3214
|
state.usedByName = {};
|
|
3215
|
+
const checkLookupRules = config.checkCompilerLookupRules;
|
|
3216
|
+
config.checkCompilerLookupRules = "OFF";
|
|
3180
3217
|
let again = false;
|
|
3181
3218
|
const optimizeCallHelper = (istate, call, node) => {
|
|
3182
3219
|
const result = optimizeCall(istate, call, node);
|
|
@@ -3749,6 +3786,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3749
3786
|
return ret;
|
|
3750
3787
|
});
|
|
3751
3788
|
});
|
|
3789
|
+
config.checkCompilerLookupRules = checkLookupRules;
|
|
3752
3790
|
reportMissingSymbols(state, config);
|
|
3753
3791
|
if (state.inlineDiagnostics) {
|
|
3754
3792
|
if (!state.diagnostics) {
|
|
@@ -3970,7 +4008,9 @@ function pragmaChecker(state, ast, diagnostics) {
|
|
|
3970
4008
|
if (quote === '"') {
|
|
3971
4009
|
return haystack.includes(needle);
|
|
3972
4010
|
}
|
|
3973
|
-
const re = new RegExp(needle.replace(/@([-\d.\w]+|"[^"]*")/g, (_match, pat) => `(?:${pat}|pre_${pat
|
|
4011
|
+
const re = new RegExp(needle.replace(/@([-\d.\w]+|"[^"]*")/g, (_match, pat) => `(?:${pat}|pre_${pat
|
|
4012
|
+
.replace(/^([a-zA-Z_]+\.)*/, "")
|
|
4013
|
+
.replace(/\W/g, "_")}(?:_\\d+)?)`));
|
|
3974
4014
|
return re.test(haystack);
|
|
3975
4015
|
};
|
|
3976
4016
|
next();
|
|
@@ -4167,7 +4207,9 @@ function sizeBasedPRE(state, func) {
|
|
|
4167
4207
|
applyReplacements(func.node, nodeMap, declMap);
|
|
4168
4208
|
func.node.body.body.unshift(variableDecl);
|
|
4169
4209
|
}
|
|
4170
|
-
|
|
4210
|
+
if (state.config?.minimizeLocals ?? true) {
|
|
4211
|
+
minimizeLocals(state, func);
|
|
4212
|
+
}
|
|
4171
4213
|
}
|
|
4172
4214
|
function buildPREGraph(state, func) {
|
|
4173
4215
|
const result = buildDataFlowGraph(state, func, (literal) => refCost(literal) > LocalRefCost, true, false);
|
|
@@ -4825,18 +4867,20 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
4825
4867
|
/* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5101);
|
|
4826
4868
|
/* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8180);
|
|
4827
4869
|
/* harmony import */ var _function_info__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(819);
|
|
4828
|
-
/* harmony import */ var
|
|
4829
|
-
/* harmony import */ var
|
|
4830
|
-
/* harmony import */ var
|
|
4831
|
-
/* harmony import */ var
|
|
4832
|
-
/* harmony import */ var
|
|
4833
|
-
/* harmony import */ var
|
|
4834
|
-
/* harmony import */ var
|
|
4835
|
-
/* harmony import */ var
|
|
4836
|
-
/* harmony import */ var
|
|
4837
|
-
/* harmony import */ var
|
|
4838
|
-
/* harmony import */ var
|
|
4839
|
-
/* harmony import */ var
|
|
4870
|
+
/* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(333);
|
|
4871
|
+
/* harmony import */ var _optimizer_types__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(9697);
|
|
4872
|
+
/* harmony import */ var _type_flow_could_be__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(4055);
|
|
4873
|
+
/* harmony import */ var _type_flow_dead_store__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(8486);
|
|
4874
|
+
/* harmony import */ var _type_flow_interp__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(7161);
|
|
4875
|
+
/* harmony import */ var _type_flow_interp_call__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(2222);
|
|
4876
|
+
/* harmony import */ var _type_flow_intersection_type__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(6973);
|
|
4877
|
+
/* harmony import */ var _type_flow_sub_type__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(9234);
|
|
4878
|
+
/* harmony import */ var _type_flow_type_flow_util__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(1638);
|
|
4879
|
+
/* harmony import */ var _type_flow_types__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(7255);
|
|
4880
|
+
/* harmony import */ var _type_flow_union_type__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(757);
|
|
4881
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(6906);
|
|
4882
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_17___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_17__);
|
|
4883
|
+
|
|
4840
4884
|
|
|
4841
4885
|
|
|
4842
4886
|
|
|
@@ -4875,15 +4919,24 @@ function buildTypeInfo(state, func, optimizeEquivalencies) {
|
|
|
4875
4919
|
const logThisRun = logging && loggingEnabledFor("TYPEFLOW_FUNC", func);
|
|
4876
4920
|
while (true) {
|
|
4877
4921
|
const { graph } = buildDataFlowGraph(state, func, () => false, false, true);
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4922
|
+
let copyPropStores;
|
|
4923
|
+
if (optimizeEquivalencies) {
|
|
4924
|
+
const result = eliminateDeadStores(state, func, graph, logThisRun);
|
|
4925
|
+
if (result.changes) {
|
|
4926
|
+
/*
|
|
4927
|
+
* eliminateDeadStores can change the control flow graph,
|
|
4928
|
+
* so we need to recompute it if it did anything.
|
|
4929
|
+
*/
|
|
4930
|
+
continue;
|
|
4931
|
+
}
|
|
4932
|
+
if (result.copyPropStores.size) {
|
|
4933
|
+
copyPropStores = result.copyPropStores;
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
const result = propagateTypes({ ...state, stack: func.stack }, func, graph, optimizeEquivalencies, copyPropStores, logThisRun);
|
|
4937
|
+
if (!result.redo) {
|
|
4938
|
+
return result.istate;
|
|
4885
4939
|
}
|
|
4886
|
-
return propagateTypes({ ...state, stack: func.stack }, func, graph, optimizeEquivalencies, logThisRun).istate;
|
|
4887
4940
|
}
|
|
4888
4941
|
}
|
|
4889
4942
|
function buildConflictGraph(state, func) {
|
|
@@ -4891,8 +4944,8 @@ function buildConflictGraph(state, func) {
|
|
|
4891
4944
|
return;
|
|
4892
4945
|
const logThisRun = logging && loggingEnabledFor("CONFLICT_FUNC", func);
|
|
4893
4946
|
const { graph, identifiers } = buildDataFlowGraph(state, func, () => false, false, true);
|
|
4894
|
-
const { nodeEquivs } = propagateTypes({ ...state, stack: func.stack }, func, graph, false,
|
|
4895
|
-
const { locals, localConflicts } = findDeadStores(func, graph, nodeEquivs, logThisRun);
|
|
4947
|
+
const { nodeEquivs } = propagateTypes({ ...state, stack: func.stack }, func, graph, false, undefined, logThisRun);
|
|
4948
|
+
const { locals, localConflicts } = findDeadStores(func, graph, nodeEquivs, false, logThisRun);
|
|
4896
4949
|
return { graph, localConflicts, locals, identifiers, logThisRun };
|
|
4897
4950
|
}
|
|
4898
4951
|
function addEquiv(ts, key, equiv) {
|
|
@@ -4903,74 +4956,43 @@ function addEquiv(ts, key, equiv) {
|
|
|
4903
4956
|
if (!keyVal || !equivVal)
|
|
4904
4957
|
return false;
|
|
4905
4958
|
if (equivVal.equivSet) {
|
|
4906
|
-
if (
|
|
4907
|
-
|
|
4908
|
-
// equiv is part of it.
|
|
4909
|
-
let s = keyVal.equivSet.next;
|
|
4910
|
-
do {
|
|
4911
|
-
if (s === equiv) {
|
|
4912
|
-
// these two are already equivalent
|
|
4913
|
-
return true;
|
|
4914
|
-
}
|
|
4915
|
-
const next = ts.get(s);
|
|
4916
|
-
if (!next || !next.equivSet) {
|
|
4917
|
-
throw new Error(`Inconsistent equivSet for ${tsKey(key)}: missing value for ${tsKey(s)}`);
|
|
4918
|
-
}
|
|
4919
|
-
s = next.equivSet.next;
|
|
4920
|
-
} while (s !== key);
|
|
4959
|
+
if (equivVal.equivSet.has(key)) {
|
|
4960
|
+
return true;
|
|
4921
4961
|
}
|
|
4922
4962
|
// equiv is already a member of a set. remove it
|
|
4923
4963
|
removeEquiv(ts, equiv);
|
|
4924
4964
|
}
|
|
4925
4965
|
// equiv is not (or no longer) part of an equivSet
|
|
4926
|
-
keyVal = { ...keyVal };
|
|
4927
4966
|
if (!keyVal.equivSet) {
|
|
4928
|
-
keyVal
|
|
4967
|
+
keyVal = { ...keyVal };
|
|
4968
|
+
keyVal.equivSet = new Set([key, equiv]);
|
|
4969
|
+
ts.set(key, keyVal);
|
|
4970
|
+
}
|
|
4971
|
+
else {
|
|
4972
|
+
keyVal.equivSet.add(equiv);
|
|
4929
4973
|
}
|
|
4930
4974
|
equivVal = { ...equivVal, equivSet: keyVal.equivSet };
|
|
4931
|
-
keyVal.equivSet = { next: equiv };
|
|
4932
|
-
ts.set(key, keyVal);
|
|
4933
4975
|
ts.set(equiv, equivVal);
|
|
4934
4976
|
return false;
|
|
4935
4977
|
}
|
|
4936
4978
|
function removeEquiv(ts, equiv) {
|
|
4937
|
-
|
|
4979
|
+
let equivVal = ts.get(equiv);
|
|
4938
4980
|
if (!equivVal?.equivSet)
|
|
4939
4981
|
return;
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
const
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
ts.set(s, rest);
|
|
4951
|
-
}
|
|
4952
|
-
else {
|
|
4953
|
-
ts.set(s, { ...rest, equivSet: equivVal.equivSet });
|
|
4954
|
-
}
|
|
4955
|
-
break;
|
|
4956
|
-
}
|
|
4957
|
-
s = next.equivSet.next;
|
|
4958
|
-
} while (true);
|
|
4959
|
-
const newVal = { ...equivVal };
|
|
4960
|
-
delete newVal.equivSet;
|
|
4961
|
-
ts.set(equiv, newVal);
|
|
4982
|
+
equivVal.equivSet.delete(equiv);
|
|
4983
|
+
if (equivVal.equivSet.size === 1) {
|
|
4984
|
+
const other = Array.from(equivVal.equivSet)[0];
|
|
4985
|
+
const otherVal = { ...ts.get(other) };
|
|
4986
|
+
delete otherVal.equivSet;
|
|
4987
|
+
ts.set(other, otherVal);
|
|
4988
|
+
}
|
|
4989
|
+
equivVal = { ...equivVal };
|
|
4990
|
+
delete equivVal.equivSet;
|
|
4991
|
+
ts.set(equiv, equivVal);
|
|
4962
4992
|
}
|
|
4963
4993
|
function getEquivSet(ts, k) {
|
|
4964
|
-
const keys =
|
|
4965
|
-
|
|
4966
|
-
do {
|
|
4967
|
-
const next = ts.get(s);
|
|
4968
|
-
if (!next || !next.equivSet) {
|
|
4969
|
-
throw new Error(`Inconsistent equivSet for ${tsKey(k)}: missing value for ${tsKey(s)}`);
|
|
4970
|
-
}
|
|
4971
|
-
keys.add(s);
|
|
4972
|
-
s = next.equivSet.next;
|
|
4973
|
-
} while (s !== k);
|
|
4994
|
+
const keys = ts.get(k)?.equivSet;
|
|
4995
|
+
assert(keys);
|
|
4974
4996
|
return keys;
|
|
4975
4997
|
}
|
|
4976
4998
|
function intersectEquiv(ts1, ts2, k) {
|
|
@@ -4982,21 +5004,26 @@ function intersectEquiv(ts1, ts2, k) {
|
|
|
4982
5004
|
removeEquiv(ts1, k);
|
|
4983
5005
|
return true;
|
|
4984
5006
|
}
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
if (!keys.has(s)) {
|
|
4994
|
-
ret = true;
|
|
4995
|
-
removeEquiv(ts1, s);
|
|
5007
|
+
let removed = null;
|
|
5008
|
+
eq1.equivSet.forEach((key) => {
|
|
5009
|
+
if (!eq2.equivSet.has(key)) {
|
|
5010
|
+
eq1.equivSet.delete(key);
|
|
5011
|
+
if (!removed) {
|
|
5012
|
+
removed = new Set();
|
|
5013
|
+
}
|
|
5014
|
+
removed.add(key);
|
|
4996
5015
|
}
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5016
|
+
});
|
|
5017
|
+
if (eq1.equivSet.size === 1) {
|
|
5018
|
+
assert(eq1.equivSet.has(k));
|
|
5019
|
+
delete eq1.equivSet;
|
|
5020
|
+
}
|
|
5021
|
+
if (removed) {
|
|
5022
|
+
removed.forEach((k) => removed.size === 1
|
|
5023
|
+
? delete ts1.get(k).equivSet
|
|
5024
|
+
: (ts1.get(k).equivSet = removed));
|
|
5025
|
+
}
|
|
5026
|
+
return false;
|
|
5000
5027
|
}
|
|
5001
5028
|
function clearAssocPaths(blockState, decl, v) {
|
|
5002
5029
|
if (v.assocPaths?.size) {
|
|
@@ -5012,12 +5039,24 @@ function clearAssocPaths(blockState, decl, v) {
|
|
|
5012
5039
|
}
|
|
5013
5040
|
}
|
|
5014
5041
|
function cloneTypeState(blockState) {
|
|
5015
|
-
const { map, trackedMemberDecls, ...rest } = blockState;
|
|
5042
|
+
const { map, trackedMemberDecls, liveCopyPropEvents, ...rest } = blockState;
|
|
5016
5043
|
const clone = { map: new Map(map), ...rest };
|
|
5044
|
+
clone.map.forEach((value, key) => {
|
|
5045
|
+
if (value.equivSet && key === Array.from(value.equivSet)[0]) {
|
|
5046
|
+
const equivSet = new Set(value.equivSet);
|
|
5047
|
+
equivSet.forEach((k) => clone.map.set(k, { ...clone.map.get(k), equivSet }));
|
|
5048
|
+
}
|
|
5049
|
+
});
|
|
5017
5050
|
if (trackedMemberDecls) {
|
|
5018
5051
|
clone.trackedMemberDecls = new Map();
|
|
5019
5052
|
trackedMemberDecls.forEach((value, key) => {
|
|
5020
|
-
clone.trackedMemberDecls.set(key, new
|
|
5053
|
+
clone.trackedMemberDecls.set(key, new Set(value));
|
|
5054
|
+
});
|
|
5055
|
+
}
|
|
5056
|
+
if (liveCopyPropEvents) {
|
|
5057
|
+
clone.liveCopyPropEvents = new Map();
|
|
5058
|
+
liveCopyPropEvents.forEach((value, key) => {
|
|
5059
|
+
clone.liveCopyPropEvents?.set(key, new Set(value));
|
|
5021
5060
|
});
|
|
5022
5061
|
}
|
|
5023
5062
|
return clone;
|
|
@@ -5029,18 +5068,84 @@ function addTrackedMemberDecl(blockState, key, assocKey) {
|
|
|
5029
5068
|
assocKey.split(".").forEach((pathItem) => {
|
|
5030
5069
|
const entries = blockState.trackedMemberDecls.get(pathItem);
|
|
5031
5070
|
if (!entries) {
|
|
5032
|
-
blockState.trackedMemberDecls.set(pathItem, new
|
|
5071
|
+
blockState.trackedMemberDecls.set(pathItem, new Set([key]));
|
|
5033
5072
|
return;
|
|
5034
5073
|
}
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5074
|
+
entries.add(key);
|
|
5075
|
+
});
|
|
5076
|
+
}
|
|
5077
|
+
function addCopyPropEvent(blockState, item) {
|
|
5078
|
+
if (!blockState.liveCopyPropEvents) {
|
|
5079
|
+
blockState.liveCopyPropEvents = new Map();
|
|
5080
|
+
}
|
|
5081
|
+
const liveCopyPropEvents = blockState.liveCopyPropEvents;
|
|
5082
|
+
const decl = item.event.decl;
|
|
5083
|
+
assert(declIsLocal(decl));
|
|
5084
|
+
let tov = blockState.map.get(decl);
|
|
5085
|
+
assert(tov);
|
|
5086
|
+
if (tov.copyPropItem !== item) {
|
|
5087
|
+
tov = { ...tov, copyPropItem: item };
|
|
5088
|
+
blockState.map.set(decl, tov);
|
|
5089
|
+
}
|
|
5090
|
+
item.contained.forEach((value, key) => {
|
|
5091
|
+
const decls = liveCopyPropEvents.get(key);
|
|
5092
|
+
if (!decls) {
|
|
5093
|
+
liveCopyPropEvents.set(key, new Set([decl]));
|
|
5094
|
+
}
|
|
5095
|
+
else {
|
|
5096
|
+
decls.add(decl);
|
|
5039
5097
|
}
|
|
5040
|
-
entry.add(assocKey);
|
|
5041
5098
|
});
|
|
5042
5099
|
}
|
|
5043
|
-
function
|
|
5100
|
+
function clearCopyProp(blockState, item) {
|
|
5101
|
+
const liveCopyPropEvents = blockState.liveCopyPropEvents;
|
|
5102
|
+
assert(liveCopyPropEvents);
|
|
5103
|
+
const itemDecl = item.event.decl;
|
|
5104
|
+
item.contained.forEach((event, decl) => {
|
|
5105
|
+
const decls = liveCopyPropEvents.get(decl);
|
|
5106
|
+
assert(decls && decls.has(itemDecl));
|
|
5107
|
+
decls.delete(itemDecl);
|
|
5108
|
+
});
|
|
5109
|
+
}
|
|
5110
|
+
function copyPropFailed(blockState, item, nodeCopyProp) {
|
|
5111
|
+
clearCopyProp(blockState, item);
|
|
5112
|
+
const ref = nodeCopyProp.get(item.event.node);
|
|
5113
|
+
if (ref) {
|
|
5114
|
+
nodeCopyProp.delete(ref);
|
|
5115
|
+
}
|
|
5116
|
+
nodeCopyProp.set(item.event.node, false);
|
|
5117
|
+
}
|
|
5118
|
+
function clearRelatedCopyPropEvents(blockState, decl, nodeCopyProp) {
|
|
5119
|
+
blockState.liveCopyPropEvents
|
|
5120
|
+
?.get(decl)
|
|
5121
|
+
?.forEach((cpDecl) => {
|
|
5122
|
+
let value = blockState.map.get(cpDecl);
|
|
5123
|
+
assert(value && value.copyPropItem);
|
|
5124
|
+
assert(Array.from(value.copyPropItem.contained).some(([key]) => {
|
|
5125
|
+
return decl === key;
|
|
5126
|
+
}));
|
|
5127
|
+
copyPropFailed(blockState, value.copyPropItem, nodeCopyProp);
|
|
5128
|
+
value = { ...value };
|
|
5129
|
+
delete value.copyPropItem;
|
|
5130
|
+
blockState.map.set(cpDecl, value);
|
|
5131
|
+
});
|
|
5132
|
+
}
|
|
5133
|
+
function validateTypeState(curState) {
|
|
5134
|
+
curState.liveCopyPropEvents?.forEach((decls) => decls.forEach((cpDecl) => {
|
|
5135
|
+
const value = curState.map.get(cpDecl);
|
|
5136
|
+
assert(value && value.copyPropItem);
|
|
5137
|
+
}));
|
|
5138
|
+
curState.trackedMemberDecls?.forEach((affected, key) => {
|
|
5139
|
+
affected.forEach((decl) => {
|
|
5140
|
+
const value = curState.map.get(decl);
|
|
5141
|
+
assert(value && value.assocPaths);
|
|
5142
|
+
if (!Array.from(value.assocPaths).some((path) => `.${path}.`.includes(`.${key}.`))) {
|
|
5143
|
+
throw new Error("What");
|
|
5144
|
+
}
|
|
5145
|
+
});
|
|
5146
|
+
});
|
|
5147
|
+
}
|
|
5148
|
+
function mergeTypeState(blockStates, index, from, nodeCopyProp) {
|
|
5044
5149
|
const to = blockStates[index];
|
|
5045
5150
|
if (!to) {
|
|
5046
5151
|
blockStates[index] = cloneTypeState(from);
|
|
@@ -5048,9 +5153,9 @@ function mergeTypeState(blockStates, index, from) {
|
|
|
5048
5153
|
return true;
|
|
5049
5154
|
}
|
|
5050
5155
|
const widen = ++to.visits > 10;
|
|
5051
|
-
// we'll rebuild
|
|
5052
|
-
// addTrackedMemberDecl below.
|
|
5156
|
+
// we'll rebuild these from scratch
|
|
5053
5157
|
delete to.trackedMemberDecls;
|
|
5158
|
+
delete to.liveCopyPropEvents;
|
|
5054
5159
|
let changes = false;
|
|
5055
5160
|
to.map.forEach((tov, k) => {
|
|
5056
5161
|
const fromv = from.map.get(k);
|
|
@@ -5069,25 +5174,57 @@ function mergeTypeState(blockStates, index, from) {
|
|
|
5069
5174
|
}
|
|
5070
5175
|
}
|
|
5071
5176
|
if (tov.assocPaths) {
|
|
5072
|
-
const assocPaths = new Set(tov.assocPaths);
|
|
5073
5177
|
tov = { ...tov };
|
|
5074
5178
|
if (!fromv.assocPaths) {
|
|
5075
5179
|
changes = true;
|
|
5076
5180
|
delete tov.assocPaths;
|
|
5077
5181
|
}
|
|
5078
5182
|
else {
|
|
5183
|
+
const assocPaths = new Set(tov.assocPaths);
|
|
5079
5184
|
assocPaths.forEach((key) => {
|
|
5080
5185
|
if (!fromv.assocPaths.has(key)) {
|
|
5081
5186
|
assocPaths.delete(key);
|
|
5187
|
+
changes = true;
|
|
5082
5188
|
}
|
|
5083
5189
|
else {
|
|
5084
5190
|
addTrackedMemberDecl(to, k, key);
|
|
5085
5191
|
}
|
|
5086
5192
|
});
|
|
5087
|
-
|
|
5193
|
+
if (assocPaths.size) {
|
|
5194
|
+
tov.assocPaths = assocPaths;
|
|
5195
|
+
}
|
|
5196
|
+
else {
|
|
5197
|
+
delete tov.assocPaths;
|
|
5198
|
+
}
|
|
5088
5199
|
}
|
|
5089
5200
|
to.map.set(k, tov);
|
|
5090
5201
|
}
|
|
5202
|
+
// if both from and to have copyPropEvents, we can only
|
|
5203
|
+
// keep it if they're the same event.
|
|
5204
|
+
if (tov.copyPropItem) {
|
|
5205
|
+
if (tov.copyPropItem.event !== fromv.copyPropItem?.event) {
|
|
5206
|
+
const toProp = nodeCopyProp.get(tov.copyPropItem.event.node);
|
|
5207
|
+
if (toProp) {
|
|
5208
|
+
nodeCopyProp.delete(toProp);
|
|
5209
|
+
}
|
|
5210
|
+
nodeCopyProp.set(tov.copyPropItem.event.node, false);
|
|
5211
|
+
if (fromv.copyPropItem) {
|
|
5212
|
+
const fromProp = nodeCopyProp.get(fromv.copyPropItem.event.node);
|
|
5213
|
+
if (fromProp) {
|
|
5214
|
+
nodeCopyProp.delete(fromProp);
|
|
5215
|
+
}
|
|
5216
|
+
nodeCopyProp.set(fromv.copyPropItem.event.node, false);
|
|
5217
|
+
}
|
|
5218
|
+
tov = { ...tov };
|
|
5219
|
+
delete tov.copyPropItem;
|
|
5220
|
+
changes = true;
|
|
5221
|
+
to.map.set(k, tov);
|
|
5222
|
+
}
|
|
5223
|
+
else {
|
|
5224
|
+
assert(k === tov.copyPropItem.event.decl);
|
|
5225
|
+
addCopyPropEvent(to, tov.copyPropItem);
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5091
5228
|
if (widen) {
|
|
5092
5229
|
if (subtypeOf(fromv.curType, tov.curType))
|
|
5093
5230
|
return;
|
|
@@ -5110,19 +5247,6 @@ function mergeTypeState(blockStates, index, from) {
|
|
|
5110
5247
|
});
|
|
5111
5248
|
return changes;
|
|
5112
5249
|
}
|
|
5113
|
-
function tsEquivs(state, key) {
|
|
5114
|
-
const result = [];
|
|
5115
|
-
let s = key;
|
|
5116
|
-
do {
|
|
5117
|
-
result.push(tsKey(s));
|
|
5118
|
-
const next = state.get(s);
|
|
5119
|
-
if (!next || !next.equivSet) {
|
|
5120
|
-
throw new Error(`Inconsistent equivSet for ${tsKey(key)}: missing value for ${tsKey(s)}`);
|
|
5121
|
-
}
|
|
5122
|
-
s = next.equivSet.next;
|
|
5123
|
-
} while (s !== key);
|
|
5124
|
-
return `[(${result.join(", ")})]`;
|
|
5125
|
-
}
|
|
5126
5250
|
function typeStateEntry(value, key) {
|
|
5127
5251
|
return `${tsKey(key)} = ${display(value.curType)}`;
|
|
5128
5252
|
}
|
|
@@ -5133,21 +5257,26 @@ function printBlockState(block, state, indent = "") {
|
|
|
5133
5257
|
return;
|
|
5134
5258
|
}
|
|
5135
5259
|
state.map.forEach((value, key) => {
|
|
5136
|
-
console.log(`${indent} - ${typeStateEntry(value, key)}${value.equivSet
|
|
5260
|
+
console.log(`${indent} - ${typeStateEntry(value, key)}${value.equivSet
|
|
5261
|
+
? " " + `[(${Array.from(value.equivSet).map(tsKey).join(", ")})]`
|
|
5262
|
+
: ""}`);
|
|
5137
5263
|
});
|
|
5138
5264
|
}
|
|
5139
5265
|
function updateAffected(blockState, objectType, baseDecl, assignedPath, affectedName, affected, assignedType) {
|
|
5140
|
-
affected.forEach((
|
|
5266
|
+
affected.forEach((key) => {
|
|
5267
|
+
let droppedComponents = null;
|
|
5141
5268
|
const entry = blockState.map.get(key);
|
|
5142
|
-
assert(entry);
|
|
5269
|
+
assert(entry && entry.assocPaths);
|
|
5143
5270
|
let newEntry = entry;
|
|
5144
|
-
|
|
5271
|
+
entry.assocPaths.forEach((path) => {
|
|
5145
5272
|
if (key === baseDecl && path === assignedPath) {
|
|
5146
5273
|
return;
|
|
5147
5274
|
}
|
|
5148
|
-
assert(entry.assocPaths?.has(path));
|
|
5149
|
-
const assocPath = [];
|
|
5150
5275
|
const pathSegments = path.split(".");
|
|
5276
|
+
if (!pathSegments.includes(affectedName)) {
|
|
5277
|
+
return;
|
|
5278
|
+
}
|
|
5279
|
+
const assocPath = [];
|
|
5151
5280
|
let type = entry.curType;
|
|
5152
5281
|
for (let i = 0; i < pathSegments.length; i++) {
|
|
5153
5282
|
const pathItem = pathSegments[i];
|
|
@@ -5157,20 +5286,25 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
|
|
|
5157
5286
|
});
|
|
5158
5287
|
if (pathItem === affectedName && couldBeShallow(type, objectType)) {
|
|
5159
5288
|
const newAssocKey = assocPath.map((av) => av.name ?? "*").join(".");
|
|
5160
|
-
const baseType = updateByAssocPath(assocPath, assignedType, true);
|
|
5161
5289
|
if (newEntry === entry) {
|
|
5162
5290
|
newEntry = { ...entry };
|
|
5163
5291
|
}
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
newEntry.assocPaths = new Set(entry.assocPaths);
|
|
5292
|
+
if (newAssocKey !== path) {
|
|
5293
|
+
newEntry.assocPaths = new Set(newEntry.assocPaths);
|
|
5167
5294
|
newEntry.assocPaths.delete(path);
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5295
|
+
// the "extra" path components will also have entries
|
|
5296
|
+
// in blockState.trackedMemberDecls. Since they're gone
|
|
5297
|
+
// from here, we (may) need to remove them from there
|
|
5298
|
+
if (!droppedComponents) {
|
|
5299
|
+
droppedComponents = new Set();
|
|
5300
|
+
}
|
|
5301
|
+
while (++i < pathSegments.length) {
|
|
5302
|
+
droppedComponents.add(pathSegments[i]);
|
|
5303
|
+
}
|
|
5304
|
+
break;
|
|
5173
5305
|
}
|
|
5306
|
+
const baseType = updateByAssocPath(assocPath, assignedType, true);
|
|
5307
|
+
newEntry.curType = baseType;
|
|
5174
5308
|
break;
|
|
5175
5309
|
}
|
|
5176
5310
|
if (pathItem === "*") {
|
|
@@ -5201,11 +5335,17 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
|
|
|
5201
5335
|
}
|
|
5202
5336
|
});
|
|
5203
5337
|
if (newEntry !== entry) {
|
|
5338
|
+
if (droppedComponents) {
|
|
5339
|
+
newEntry.assocPaths.forEach((path) => path
|
|
5340
|
+
.split(".")
|
|
5341
|
+
.forEach((pathComponent) => droppedComponents.delete(pathComponent)));
|
|
5342
|
+
droppedComponents.forEach((pathComponent) => blockState.trackedMemberDecls.get(pathComponent).delete(key));
|
|
5343
|
+
}
|
|
5204
5344
|
blockState.map.set(key, newEntry);
|
|
5205
5345
|
}
|
|
5206
5346
|
});
|
|
5207
5347
|
}
|
|
5208
|
-
function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
5348
|
+
function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStores, logThisRun) {
|
|
5209
5349
|
// We want to traverse the blocks in reverse post order, in
|
|
5210
5350
|
// order to propagate the "availability" of the types.
|
|
5211
5351
|
const order = getPostOrder(graph).reverse();
|
|
@@ -5227,7 +5367,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5227
5367
|
printBlockTrailer(block);
|
|
5228
5368
|
});
|
|
5229
5369
|
}
|
|
5230
|
-
function memberDeclInfo(blockState, decl,
|
|
5370
|
+
function memberDeclInfo(blockState, decl, newValue) {
|
|
5231
5371
|
const baseType = getStateType(blockState, decl.base);
|
|
5232
5372
|
const typePath = [baseType];
|
|
5233
5373
|
let next = null;
|
|
@@ -5307,13 +5447,11 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5307
5447
|
}));
|
|
5308
5448
|
const assocKey = assocValue.map((av) => av.name ?? "*").join(".");
|
|
5309
5449
|
const newType = updateByAssocPath(assocValue, next, false);
|
|
5310
|
-
setStateEvent(blockState, decl.base, newType,
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
const tsv = blockState.map.get(decl.base);
|
|
5314
|
-
if (!tsv.assocPaths)
|
|
5315
|
-
tsv.assocPaths = new Set();
|
|
5450
|
+
setStateEvent(blockState, decl.base, newType, newValue ? 1 /* UpdateKind.Inner */ : 0 /* UpdateKind.None */);
|
|
5451
|
+
const tsv = { ...blockState.map.get(decl.base) };
|
|
5452
|
+
tsv.assocPaths = new Set(tsv.assocPaths);
|
|
5316
5453
|
tsv.assocPaths.add(assocKey);
|
|
5454
|
+
blockState.map.set(decl.base, tsv);
|
|
5317
5455
|
addTrackedMemberDecl(blockState, decl.base, assocKey);
|
|
5318
5456
|
if (newValue) {
|
|
5319
5457
|
const baseElem = assocValue[decl.path.length - 1];
|
|
@@ -5341,7 +5479,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5341
5479
|
const doUpdate = (key, cur) => {
|
|
5342
5480
|
const update = cloneType(cur.curType);
|
|
5343
5481
|
unionInto(update, next);
|
|
5344
|
-
setStateEvent(blockState, key, update,
|
|
5482
|
+
setStateEvent(blockState, key, update, 0 /* UpdateKind.None */);
|
|
5345
5483
|
};
|
|
5346
5484
|
if (decls.length === 1) {
|
|
5347
5485
|
const cur = blockState.map.get(decls[0]);
|
|
@@ -5375,44 +5513,57 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5375
5513
|
return cur;
|
|
5376
5514
|
}, { type: 0 /* TypeTag.Never */ });
|
|
5377
5515
|
}
|
|
5378
|
-
function setStateEvent(blockState, decl, value,
|
|
5516
|
+
function setStateEvent(blockState, decl, value, updateKind) {
|
|
5379
5517
|
if (Array.isArray(decl) ||
|
|
5380
5518
|
(decl.type !== "MemberDecl" && decl.type !== "Unknown")) {
|
|
5381
|
-
|
|
5382
|
-
|
|
5519
|
+
if (updateKind !== 0 /* UpdateKind.None */) {
|
|
5520
|
+
// even if we're only modifying an Object, rather
|
|
5521
|
+
// than reassigning it, we need to clear the
|
|
5522
|
+
// related copy prop events, because although
|
|
5523
|
+
// we may only see the object itself, the expression
|
|
5524
|
+
// could access its fields. eg if x is the decl, then
|
|
5525
|
+
//
|
|
5526
|
+
// foo(x);
|
|
5527
|
+
//
|
|
5528
|
+
// might change when x.a.b.c is changed, even if we know
|
|
5529
|
+
// that foo is side-effect free, and accesses no globals.
|
|
5530
|
+
clearRelatedCopyPropEvents(blockState, decl, nodeCopyProp);
|
|
5531
|
+
}
|
|
5532
|
+
if (updateKind !== 2 /* UpdateKind.Reassign */) {
|
|
5383
5533
|
/*
|
|
5384
|
-
* If we're not
|
|
5385
|
-
* must be applied to every
|
|
5534
|
+
* If we're not re-assigning, the equivalencies don't
|
|
5535
|
+
* change, so this update must be applied to every
|
|
5536
|
+
* element of the set
|
|
5386
5537
|
*/
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5538
|
+
const v = blockState.map.get(decl);
|
|
5539
|
+
if (v?.equivSet) {
|
|
5540
|
+
v.equivSet.forEach((s) => {
|
|
5390
5541
|
const next = blockState.map.get(s);
|
|
5391
|
-
|
|
5392
|
-
throw new Error(`Inconsistent equivSet for ${tsKey(decl)}: missing value for ${tsKey(s)}`);
|
|
5393
|
-
}
|
|
5542
|
+
assert(next && next.equivSet?.has(s));
|
|
5394
5543
|
blockState.map.set(s, { ...next, curType: value });
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5544
|
+
});
|
|
5545
|
+
}
|
|
5546
|
+
else {
|
|
5547
|
+
blockState.map.set(decl, { ...v, curType: value });
|
|
5398
5548
|
}
|
|
5399
5549
|
}
|
|
5400
5550
|
else {
|
|
5551
|
+
const v = blockState.map.get(decl);
|
|
5401
5552
|
removeEquiv(blockState.map, decl);
|
|
5402
|
-
|
|
5403
|
-
if (v.assocPaths?.size) {
|
|
5553
|
+
if (v?.assocPaths?.size) {
|
|
5404
5554
|
clearAssocPaths(blockState, decl, v);
|
|
5405
|
-
delete v.assocPaths;
|
|
5406
5555
|
}
|
|
5556
|
+
if (v?.copyPropItem) {
|
|
5557
|
+
copyPropFailed(blockState, v.copyPropItem, nodeCopyProp);
|
|
5558
|
+
}
|
|
5559
|
+
blockState.map.set(decl, { curType: value });
|
|
5407
5560
|
}
|
|
5408
|
-
v.curType = value;
|
|
5409
|
-
blockState.map.set(decl, v);
|
|
5410
5561
|
return;
|
|
5411
5562
|
}
|
|
5412
5563
|
if (decl.type === "Unknown") {
|
|
5413
5564
|
return;
|
|
5414
5565
|
}
|
|
5415
|
-
return memberDeclInfo(blockState, decl,
|
|
5566
|
+
return memberDeclInfo(blockState, decl, value)?.[1];
|
|
5416
5567
|
}
|
|
5417
5568
|
function getStateType(blockState, decl) {
|
|
5418
5569
|
return getStateEntry(blockState, decl).curType;
|
|
@@ -5430,7 +5581,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5430
5581
|
if (decl.type === "Unknown") {
|
|
5431
5582
|
return { curType: { type: 0 /* TypeTag.Never */ } };
|
|
5432
5583
|
}
|
|
5433
|
-
const info = memberDeclInfo(blockState, decl
|
|
5584
|
+
const info = memberDeclInfo(blockState, decl);
|
|
5434
5585
|
return { curType: info ? info[0] : { type: 524287 /* TypeTag.Any */ } };
|
|
5435
5586
|
}
|
|
5436
5587
|
const blockStates = [];
|
|
@@ -5451,23 +5602,58 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5451
5602
|
if (succ.order == null) {
|
|
5452
5603
|
throw new Error("Unreachable block was visited");
|
|
5453
5604
|
}
|
|
5454
|
-
if (mergeTypeState(blockStates, succ.order, curState)) {
|
|
5605
|
+
if (mergeTypeState(blockStates, succ.order, curState, nodeCopyProp)) {
|
|
5606
|
+
if (logThisRun) {
|
|
5607
|
+
console.log(`re-merge: ${top.order} -> ${succ.order}`);
|
|
5608
|
+
}
|
|
5455
5609
|
queue.enqueue(succ);
|
|
5456
5610
|
}
|
|
5457
5611
|
});
|
|
5458
5612
|
};
|
|
5459
|
-
function
|
|
5613
|
+
function checkModResults(curState, calleeObjDecl, callees, node) {
|
|
5460
5614
|
const calleeObj = getStateType(curState, calleeObjDecl);
|
|
5461
|
-
|
|
5462
|
-
|
|
5615
|
+
const calleeResult = { type: 0 /* TypeTag.Never */ };
|
|
5616
|
+
const result = every(callees, (callee) => {
|
|
5617
|
+
const info = sysCallInfo(istate.state, callee);
|
|
5463
5618
|
if (!info)
|
|
5464
5619
|
return false;
|
|
5465
5620
|
const result = info(istate.state, callee, calleeObj, () => node.arguments.map((arg) => evaluateExpr(state, arg, typeMap).value));
|
|
5621
|
+
if (!result.effectFree) {
|
|
5622
|
+
return false;
|
|
5623
|
+
}
|
|
5466
5624
|
if (result.calleeObj) {
|
|
5467
|
-
|
|
5625
|
+
unionInto(calleeResult, result.calleeObj);
|
|
5468
5626
|
}
|
|
5469
5627
|
return true;
|
|
5470
5628
|
});
|
|
5629
|
+
return result ? calleeResult : null;
|
|
5630
|
+
}
|
|
5631
|
+
function modInterference(blockState, event, doUpdate, callback) {
|
|
5632
|
+
let callees = undefined;
|
|
5633
|
+
if (event.calleeDecl) {
|
|
5634
|
+
const calleeType = getStateType(blockState, event.calleeDecl);
|
|
5635
|
+
if (hasValue(calleeType) && calleeType.type === 8192 /* TypeTag.Function */) {
|
|
5636
|
+
callees = calleeType.value;
|
|
5637
|
+
}
|
|
5638
|
+
else {
|
|
5639
|
+
callees = findCalleesByNode(state, event.node);
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5642
|
+
if (callees === undefined && event.callees !== undefined) {
|
|
5643
|
+
callees = event.callees;
|
|
5644
|
+
}
|
|
5645
|
+
if (event.calleeObj) {
|
|
5646
|
+
const calleeObjResult = checkModResults(blockState, event.calleeObj, callees, event.node);
|
|
5647
|
+
if (calleeObjResult) {
|
|
5648
|
+
if (calleeObjResult.type !== 0 /* TypeTag.Never */) {
|
|
5649
|
+
if (doUpdate) {
|
|
5650
|
+
setStateEvent(blockState, event.calleeObj, calleeObjResult, 0 /* UpdateKind.None */);
|
|
5651
|
+
}
|
|
5652
|
+
}
|
|
5653
|
+
return true;
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
return callback(callees, event.calleeObj);
|
|
5471
5657
|
}
|
|
5472
5658
|
function handleFlowEvent(event, top, curState) {
|
|
5473
5659
|
const fixTypes = (equal, left, right, leftDecl, rightDecl) => {
|
|
@@ -5487,7 +5673,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5487
5673
|
}
|
|
5488
5674
|
}
|
|
5489
5675
|
const tmpState = cloneTypeState(curState);
|
|
5490
|
-
setStateEvent(tmpState, leftDecl, leftr,
|
|
5676
|
+
setStateEvent(tmpState, leftDecl, leftr, 0 /* UpdateKind.None */);
|
|
5491
5677
|
if (rightDecl) {
|
|
5492
5678
|
let rightr = restrictByEquality(left, right);
|
|
5493
5679
|
if (rightr.type === 0 /* TypeTag.Never */) {
|
|
@@ -5499,7 +5685,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5499
5685
|
return false;
|
|
5500
5686
|
}
|
|
5501
5687
|
}
|
|
5502
|
-
setStateEvent(tmpState, rightDecl, rightr,
|
|
5688
|
+
setStateEvent(tmpState, rightDecl, rightr, 0 /* UpdateKind.None */);
|
|
5503
5689
|
}
|
|
5504
5690
|
return tmpState;
|
|
5505
5691
|
}
|
|
@@ -5513,7 +5699,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5513
5699
|
if (singletonRemoved.type === 0 /* TypeTag.Never */)
|
|
5514
5700
|
return false;
|
|
5515
5701
|
const tmpState = cloneTypeState(curState);
|
|
5516
|
-
setStateEvent(tmpState, leftDecl, singletonRemoved,
|
|
5702
|
+
setStateEvent(tmpState, leftDecl, singletonRemoved, 0 /* UpdateKind.None */);
|
|
5517
5703
|
return tmpState;
|
|
5518
5704
|
}
|
|
5519
5705
|
return null;
|
|
@@ -5545,7 +5731,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5545
5731
|
if (singletonRemoved.type === 0 /* TypeTag.Never */)
|
|
5546
5732
|
return false;
|
|
5547
5733
|
const tmpState = cloneTypeState(curState);
|
|
5548
|
-
setStateEvent(tmpState, event.left, singletonRemoved,
|
|
5734
|
+
setStateEvent(tmpState, event.left, singletonRemoved, 0 /* UpdateKind.None */);
|
|
5549
5735
|
return tmpState;
|
|
5550
5736
|
}
|
|
5551
5737
|
}
|
|
@@ -5560,7 +5746,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5560
5746
|
if (nonNullRemoved.type === 0 /* TypeTag.Never */)
|
|
5561
5747
|
return false;
|
|
5562
5748
|
const tmpState = cloneTypeState(curState);
|
|
5563
|
-
setStateEvent(tmpState, event.left, nonNullRemoved,
|
|
5749
|
+
setStateEvent(tmpState, event.left, nonNullRemoved, 0 /* UpdateKind.None */);
|
|
5564
5750
|
return tmpState;
|
|
5565
5751
|
}
|
|
5566
5752
|
break;
|
|
@@ -5618,7 +5804,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5618
5804
|
}
|
|
5619
5805
|
if (result) {
|
|
5620
5806
|
const tmpState = cloneTypeState(curState);
|
|
5621
|
-
setStateEvent(tmpState, event.left, result,
|
|
5807
|
+
setStateEvent(tmpState, event.left, result, 0 /* UpdateKind.None */);
|
|
5622
5808
|
return tmpState;
|
|
5623
5809
|
}
|
|
5624
5810
|
}
|
|
@@ -5645,7 +5831,10 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5645
5831
|
console.log(` Flow (true): merge to ${trueSucc.order || -1}`);
|
|
5646
5832
|
printBlockState(top, sTrue || curState, " >true ");
|
|
5647
5833
|
}
|
|
5648
|
-
if (mergeTypeState(blockStates, trueSucc.order, sTrue || curState)) {
|
|
5834
|
+
if (mergeTypeState(blockStates, trueSucc.order, sTrue || curState, nodeCopyProp)) {
|
|
5835
|
+
if (logThisRun) {
|
|
5836
|
+
console.log(`re-merge: ${top.order} -> ${trueSucc.order}`);
|
|
5837
|
+
}
|
|
5649
5838
|
queue.enqueue(trueSucc);
|
|
5650
5839
|
}
|
|
5651
5840
|
}
|
|
@@ -5657,22 +5846,33 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5657
5846
|
console.log(` Flow (false): merge to: ${falseSucc.order || -1}`);
|
|
5658
5847
|
printBlockState(top, sFalse || curState, " >false ");
|
|
5659
5848
|
}
|
|
5660
|
-
if (mergeTypeState(blockStates, falseSucc.order, sFalse || curState)) {
|
|
5849
|
+
if (mergeTypeState(blockStates, falseSucc.order, sFalse || curState, nodeCopyProp)) {
|
|
5850
|
+
if (logThisRun) {
|
|
5851
|
+
console.log(`re-merge: ${top.order} -> ${falseSucc.order}`);
|
|
5852
|
+
}
|
|
5661
5853
|
queue.enqueue(falseSucc);
|
|
5662
5854
|
}
|
|
5663
5855
|
}
|
|
5664
5856
|
return true;
|
|
5665
5857
|
}
|
|
5858
|
+
/*
|
|
5859
|
+
* nodeCopyProp contains two related maps. It maps ref nodes
|
|
5860
|
+
* to the def node that should be copy propagated. It also maps
|
|
5861
|
+
* def nodes to false, to indicate a previous failure to find
|
|
5862
|
+
* a copy prop candidate.
|
|
5863
|
+
*/
|
|
5864
|
+
const nodeCopyProp = new Map();
|
|
5666
5865
|
const nodeEquivs = new Map();
|
|
5667
5866
|
const localDecls = new Map();
|
|
5668
5867
|
const localConflicts = new Set();
|
|
5669
5868
|
const selfAssignments = new Set();
|
|
5670
5869
|
const processEvent = (top, curState, event, skipMerge) => {
|
|
5671
5870
|
if (!skipMerge && event.mayThrow && top.exsucc) {
|
|
5672
|
-
if (mergeTypeState(blockStates, top.exsucc.order, curState)) {
|
|
5871
|
+
if (mergeTypeState(blockStates, top.exsucc.order, curState, nodeCopyProp)) {
|
|
5673
5872
|
queue.enqueue(top.exsucc);
|
|
5674
5873
|
}
|
|
5675
5874
|
}
|
|
5875
|
+
validateTypeState(curState);
|
|
5676
5876
|
switch (event.type) {
|
|
5677
5877
|
case "kil": {
|
|
5678
5878
|
const curEntry = getStateEntry(curState, event.decl);
|
|
@@ -5682,11 +5882,15 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5682
5882
|
if (curEntry.assocPaths) {
|
|
5683
5883
|
clearAssocPaths(curState, event.decl, curEntry);
|
|
5684
5884
|
}
|
|
5885
|
+
if (curEntry.copyPropItem) {
|
|
5886
|
+
copyPropFailed(curState, curEntry.copyPropItem, nodeCopyProp);
|
|
5887
|
+
}
|
|
5888
|
+
clearRelatedCopyPropEvents(curState, event.decl, nodeCopyProp);
|
|
5685
5889
|
curState.map.delete(event.decl);
|
|
5686
5890
|
break;
|
|
5687
5891
|
}
|
|
5688
5892
|
case "ref": {
|
|
5689
|
-
|
|
5893
|
+
let curEntry = getStateEntry(curState, event.decl);
|
|
5690
5894
|
typeMap.set(event.node, curEntry.curType);
|
|
5691
5895
|
nodeEquivs.delete(event.node);
|
|
5692
5896
|
if (curEntry.equivSet) {
|
|
@@ -5698,6 +5902,26 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5698
5902
|
});
|
|
5699
5903
|
}
|
|
5700
5904
|
}
|
|
5905
|
+
if (copyPropStores) {
|
|
5906
|
+
nodeCopyProp.delete(event.node);
|
|
5907
|
+
if (curEntry.copyPropItem) {
|
|
5908
|
+
const copyPropInfo = copyPropStores.get(curEntry.copyPropItem.event.node);
|
|
5909
|
+
assert(copyPropInfo && copyPropInfo.ref === event.node);
|
|
5910
|
+
const defNode = nodeCopyProp.get(curEntry.copyPropItem.event.node);
|
|
5911
|
+
assert(!defNode || defNode === event.node);
|
|
5912
|
+
if (defNode !== false) {
|
|
5913
|
+
nodeCopyProp.set(event.node, curEntry.copyPropItem.event.node);
|
|
5914
|
+
nodeCopyProp.set(curEntry.copyPropItem.event.node, event.node);
|
|
5915
|
+
}
|
|
5916
|
+
clearCopyProp(curState, curEntry.copyPropItem);
|
|
5917
|
+
curEntry = { ...curEntry };
|
|
5918
|
+
delete curEntry.copyPropItem;
|
|
5919
|
+
curState.map.set(event.decl, curEntry);
|
|
5920
|
+
}
|
|
5921
|
+
else if (declIsNonLocal(event.decl)) {
|
|
5922
|
+
clearRelatedCopyPropEvents(curState, null, nodeCopyProp);
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5701
5925
|
if (logThisRun) {
|
|
5702
5926
|
console.log(` ${describeEvent(event)} == ${display(curEntry.curType)}`);
|
|
5703
5927
|
}
|
|
@@ -5707,53 +5931,76 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5707
5931
|
if (logThisRun) {
|
|
5708
5932
|
console.log(` ${describeEvent(event)}`);
|
|
5709
5933
|
}
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
}
|
|
5719
|
-
}
|
|
5720
|
-
if (callees === undefined && event.callees !== undefined) {
|
|
5721
|
-
callees = event.callees;
|
|
5722
|
-
}
|
|
5723
|
-
if (event.calleeObj) {
|
|
5724
|
-
if (handleMod(curState, event.calleeObj, callees, event.node)) {
|
|
5725
|
-
break;
|
|
5934
|
+
modInterference(curState, event, true, (callees, calleeObj) => {
|
|
5935
|
+
clearRelatedCopyPropEvents(curState, null, nodeCopyProp);
|
|
5936
|
+
if (calleeObj) {
|
|
5937
|
+
const objType = getStateType(curState, calleeObj);
|
|
5938
|
+
if (objType.type &
|
|
5939
|
+
(32768 /* TypeTag.Object */ | 512 /* TypeTag.Array */ | 1024 /* TypeTag.Dictionary */)) {
|
|
5940
|
+
setStateEvent(curState, calleeObj, objType, 1 /* UpdateKind.Inner */);
|
|
5941
|
+
}
|
|
5726
5942
|
}
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5943
|
+
if (nodeCopyProp.size &&
|
|
5944
|
+
event.node.type === "CallExpression" &&
|
|
5945
|
+
some(callees, (callee) => inlineRequested(state, callee))) {
|
|
5946
|
+
// we don't want to copy-prop to the argument of
|
|
5947
|
+
// an inline function, because that could prevent
|
|
5948
|
+
// inlining.
|
|
5949
|
+
event.node.arguments.forEach((arg) => {
|
|
5950
|
+
const def = nodeCopyProp.get(arg);
|
|
5951
|
+
if (def && nodeCopyProp.get(def) !== false) {
|
|
5952
|
+
nodeCopyProp.set(def, false);
|
|
5953
|
+
nodeCopyProp.delete(arg);
|
|
5954
|
+
}
|
|
5955
|
+
});
|
|
5735
5956
|
}
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5957
|
+
let calleeEffects;
|
|
5958
|
+
curState.map.forEach((tsv, decl) => {
|
|
5959
|
+
let type = tsv.curType;
|
|
5960
|
+
if ((type.value == null ||
|
|
5961
|
+
!(type.type &
|
|
5962
|
+
(32768 /* TypeTag.Object */ | 512 /* TypeTag.Array */ | 1024 /* TypeTag.Dictionary */))) &&
|
|
5963
|
+
!some(decl, (d) => d.type === "VariableDeclarator" && !isLocal(d))) {
|
|
5964
|
+
return;
|
|
5739
5965
|
}
|
|
5740
|
-
if (
|
|
5741
|
-
|
|
5966
|
+
if (modifiableDecl(decl, callees)) {
|
|
5967
|
+
if (tsv.equivSet) {
|
|
5968
|
+
removeEquiv(curState.map, decl);
|
|
5969
|
+
}
|
|
5970
|
+
if (tsv.assocPaths) {
|
|
5971
|
+
clearAssocPaths(curState, decl, tsv);
|
|
5972
|
+
}
|
|
5973
|
+
// we only attach copyPropItems to locals,
|
|
5974
|
+
// which can't be modified by a call.
|
|
5975
|
+
assert(!tsv.copyPropItem);
|
|
5976
|
+
clearRelatedCopyPropEvents(curState, decl, nodeCopyProp);
|
|
5977
|
+
curState.map.set(decl, { curType: typeConstraint(decl) });
|
|
5742
5978
|
}
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
const
|
|
5752
|
-
|
|
5753
|
-
|
|
5979
|
+
else if (type.type &
|
|
5980
|
+
(32768 /* TypeTag.Object */ | 512 /* TypeTag.Array */ | 1024 /* TypeTag.Dictionary */) &&
|
|
5981
|
+
(calleeEffects == null
|
|
5982
|
+
? (calleeEffects =
|
|
5983
|
+
!callees ||
|
|
5984
|
+
!every(callees, (callee) => callee.info === false))
|
|
5985
|
+
: calleeEffects)) {
|
|
5986
|
+
if (type.value != null && type.type & 32768 /* TypeTag.Object */) {
|
|
5987
|
+
const odata = getObjectValue(tsv.curType);
|
|
5988
|
+
if (odata?.obj) {
|
|
5989
|
+
type = cloneType(type);
|
|
5990
|
+
const newData = { klass: odata.klass };
|
|
5991
|
+
setUnionComponent(type, 32768 /* TypeTag.Object */, newData);
|
|
5992
|
+
if (tsv.assocPaths) {
|
|
5993
|
+
clearAssocPaths(curState, decl, tsv);
|
|
5994
|
+
tsv = { ...tsv };
|
|
5995
|
+
delete tsv.assocPaths;
|
|
5996
|
+
}
|
|
5997
|
+
curState.map.set(decl, { ...tsv, curType: type });
|
|
5998
|
+
}
|
|
5754
5999
|
}
|
|
6000
|
+
clearRelatedCopyPropEvents(curState, decl, nodeCopyProp);
|
|
5755
6001
|
}
|
|
5756
|
-
}
|
|
6002
|
+
});
|
|
6003
|
+
return true;
|
|
5757
6004
|
});
|
|
5758
6005
|
break;
|
|
5759
6006
|
}
|
|
@@ -5764,9 +6011,18 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5764
6011
|
? event.node.left
|
|
5765
6012
|
: null;
|
|
5766
6013
|
if (lval) {
|
|
5767
|
-
const
|
|
5768
|
-
if (
|
|
5769
|
-
typeMap.set(lval,
|
|
6014
|
+
const before = getStateEntry(curState, event.decl);
|
|
6015
|
+
if (before.curType) {
|
|
6016
|
+
typeMap.set(lval, before.curType);
|
|
6017
|
+
}
|
|
6018
|
+
if (before.copyPropItem &&
|
|
6019
|
+
(event.node.type !== "AssignmentExpression" ||
|
|
6020
|
+
event.node.operator !== "=")) {
|
|
6021
|
+
copyPropFailed(curState, before.copyPropItem, nodeCopyProp);
|
|
6022
|
+
const v = { ...before };
|
|
6023
|
+
delete v.copyPropItem;
|
|
6024
|
+
assert(isTypeStateKey(event.decl));
|
|
6025
|
+
curState.map.set(event.decl, v);
|
|
5770
6026
|
}
|
|
5771
6027
|
}
|
|
5772
6028
|
const expr = event.node.type === "VariableDeclarator"
|
|
@@ -5775,7 +6031,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5775
6031
|
const type = expr
|
|
5776
6032
|
? evaluate(istate, expr).value
|
|
5777
6033
|
: { type: 524287 /* TypeTag.Any */ };
|
|
5778
|
-
const wasComputedDecl = setStateEvent(curState, event.decl, type,
|
|
6034
|
+
const wasComputedDecl = setStateEvent(curState, event.decl, type, 2 /* UpdateKind.Reassign */);
|
|
5779
6035
|
some(event.decl, (decl) => {
|
|
5780
6036
|
if (decl.type !== "VariableDeclarator" ||
|
|
5781
6037
|
decl.node.kind !== "var" ||
|
|
@@ -5806,13 +6062,16 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5806
6062
|
if (value.assocPaths) {
|
|
5807
6063
|
clearAssocPaths(curState, decls, value);
|
|
5808
6064
|
}
|
|
6065
|
+
assert(!value.copyPropItem);
|
|
5809
6066
|
curState.map.set(decls, { curType: typeConstraint(decls) });
|
|
6067
|
+
clearRelatedCopyPropEvents(curState, decls, nodeCopyProp);
|
|
5810
6068
|
}
|
|
5811
6069
|
});
|
|
5812
6070
|
}
|
|
5813
6071
|
if (event.rhs) {
|
|
5814
6072
|
const selfAssign = addEquiv(curState.map, event.rhs, event.decl);
|
|
5815
|
-
if (event.node.type === "AssignmentExpression"
|
|
6073
|
+
if (event.node.type === "AssignmentExpression" &&
|
|
6074
|
+
event.node.operator === "=") {
|
|
5816
6075
|
if (selfAssign) {
|
|
5817
6076
|
// rhs and lhs are identical
|
|
5818
6077
|
selfAssignments.add(event.node);
|
|
@@ -5822,7 +6081,91 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5822
6081
|
}
|
|
5823
6082
|
}
|
|
5824
6083
|
}
|
|
5825
|
-
if (declIsLocal(event.decl)) {
|
|
6084
|
+
if (!declIsLocal(event.decl)) {
|
|
6085
|
+
clearRelatedCopyPropEvents(curState, null, nodeCopyProp);
|
|
6086
|
+
}
|
|
6087
|
+
else {
|
|
6088
|
+
if (event.containedEvents &&
|
|
6089
|
+
copyPropStores &&
|
|
6090
|
+
nodeCopyProp.get(event.node) !== false &&
|
|
6091
|
+
(!event.rhs || !declIsLocal(event.rhs))) {
|
|
6092
|
+
const copyPropCandidate = copyPropStores.get(event.node);
|
|
6093
|
+
if (copyPropCandidate) {
|
|
6094
|
+
const contained = new Map();
|
|
6095
|
+
if (event.containedEvents.every((event) => {
|
|
6096
|
+
if (event.type === "mod") {
|
|
6097
|
+
if (modInterference(curState, event, false, () => false)) {
|
|
6098
|
+
return true;
|
|
6099
|
+
}
|
|
6100
|
+
if (!copyPropCandidate.ant ||
|
|
6101
|
+
// If the ref isn't anticipated, we can't propagate it
|
|
6102
|
+
// in case it has side effects.
|
|
6103
|
+
some(event.calleeDecl, (callee) => callee.type === "FunctionDeclaration" &&
|
|
6104
|
+
inlineRequested(state, callee))) {
|
|
6105
|
+
// Don't copy prop if the rhs is marked for
|
|
6106
|
+
// inline, because we might move it out of
|
|
6107
|
+
// assignment context, to somewhere it can't be
|
|
6108
|
+
// inlined.
|
|
6109
|
+
return false;
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
if (!event.decl ||
|
|
6113
|
+
(isTypeStateKey(event.decl) &&
|
|
6114
|
+
some(event.decl, (decl) => (decl.type === "VariableDeclarator" &&
|
|
6115
|
+
decl.node.kind === "var") ||
|
|
6116
|
+
decl.type === "BinaryExpression" ||
|
|
6117
|
+
decl.type === "Identifier"))) {
|
|
6118
|
+
const key = event.decl ?? null;
|
|
6119
|
+
if (key && declIsLocal(key)) {
|
|
6120
|
+
if (nodeCopyProp.has(event.node)) {
|
|
6121
|
+
// we might have
|
|
6122
|
+
//
|
|
6123
|
+
// var x = foo();
|
|
6124
|
+
// var y = x + 1;
|
|
6125
|
+
// bar();
|
|
6126
|
+
// return y;
|
|
6127
|
+
//
|
|
6128
|
+
// In that case, its ok to drop "x = foo()" and rewrite as y = foo() + 1"
|
|
6129
|
+
// OR its ok to drop "y = x + 1" and rewrite as "return x + 1".
|
|
6130
|
+
// But we can't do both, and rewrite as "bar(); return foo() + 1;"
|
|
6131
|
+
// So just disable copy prop for *this* node. We'll re-run and have a
|
|
6132
|
+
// second chance later.
|
|
6133
|
+
return false;
|
|
6134
|
+
}
|
|
6135
|
+
else if (event.node.type === "AssignmentExpression" &&
|
|
6136
|
+
event.node.operator !== "=" &&
|
|
6137
|
+
nodeCopyProp.has(event.node.left)) {
|
|
6138
|
+
// If we're copy proping into the lhs of an update
|
|
6139
|
+
// assignment, we're going to have to rewrite it.
|
|
6140
|
+
// similar to the above, don't also do forward copy
|
|
6141
|
+
// prop. eg
|
|
6142
|
+
//
|
|
6143
|
+
// var x = a + b;
|
|
6144
|
+
// x += c;
|
|
6145
|
+
// return x;
|
|
6146
|
+
//
|
|
6147
|
+
// becomes
|
|
6148
|
+
//
|
|
6149
|
+
// var x;
|
|
6150
|
+
// x = (a + b) + c;
|
|
6151
|
+
// return x; // <- dont propagate to here (yet), in case a+b has changed.
|
|
6152
|
+
return false;
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
const item = contained.get(key);
|
|
6156
|
+
if (!item) {
|
|
6157
|
+
contained.set(key, [event]);
|
|
6158
|
+
}
|
|
6159
|
+
else {
|
|
6160
|
+
item.push(event);
|
|
6161
|
+
}
|
|
6162
|
+
}
|
|
6163
|
+
return true;
|
|
6164
|
+
})) {
|
|
6165
|
+
addCopyPropEvent(curState, { event, contained });
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
5826
6169
|
const name = localDeclName(event.decl);
|
|
5827
6170
|
const locals = localDecls.get(name);
|
|
5828
6171
|
if (!locals) {
|
|
@@ -5870,7 +6213,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5870
6213
|
func.node.params.forEach((param) => {
|
|
5871
6214
|
setStateEvent(head, param, param.type === "BinaryExpression"
|
|
5872
6215
|
? typeFromTypespec(state, param.right)
|
|
5873
|
-
: { type: 524287 /* TypeTag.Any */ },
|
|
6216
|
+
: { type: 524287 /* TypeTag.Any */ }, 0 /* UpdateKind.None */);
|
|
5874
6217
|
});
|
|
5875
6218
|
queue.enqueue(order[0]);
|
|
5876
6219
|
while (!queue.empty()) {
|
|
@@ -5904,6 +6247,16 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5904
6247
|
printBlockTrailer(top);
|
|
5905
6248
|
}
|
|
5906
6249
|
}
|
|
6250
|
+
nodeCopyProp.forEach((value, key) => {
|
|
6251
|
+
if (key.type === "VariableDeclarator" ||
|
|
6252
|
+
key.type === "AssignmentExpression") {
|
|
6253
|
+
if (value === false) {
|
|
6254
|
+
nodeCopyProp.delete(key);
|
|
6255
|
+
return;
|
|
6256
|
+
}
|
|
6257
|
+
assert(nodeCopyProp.get(value) === key);
|
|
6258
|
+
}
|
|
6259
|
+
});
|
|
5907
6260
|
if (logThisRun) {
|
|
5908
6261
|
order.forEach((block) => {
|
|
5909
6262
|
printBlockHeader(block);
|
|
@@ -5921,9 +6274,29 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5921
6274
|
nodeEquivs.forEach((value, key) => {
|
|
5922
6275
|
console.log(`${formatAst(key)} = [${value.equiv.map((equiv) => tsKey(equiv))}] ${key.loc && key.loc.source ? ` (${sourceLocation(key.loc)})` : ""}`);
|
|
5923
6276
|
});
|
|
6277
|
+
console.log("====== Copy Prop =====");
|
|
6278
|
+
nodeCopyProp.forEach((value, key) => {
|
|
6279
|
+
assert(value !== false);
|
|
6280
|
+
if (key.type === "VariableDeclarator" ||
|
|
6281
|
+
key.type === "AssignmentExpression") {
|
|
6282
|
+
return;
|
|
6283
|
+
}
|
|
6284
|
+
assert((value.type === "VariableDeclarator" && value.init) ||
|
|
6285
|
+
value.type === "AssignmentExpression");
|
|
6286
|
+
const node = value.type === "VariableDeclarator" ? value.init : value.right;
|
|
6287
|
+
console.log(`${formatAst(key)} = [${formatAstLongLines(node)}] ${key.loc && key.loc.source ? ` (${sourceLocation(key.loc)})` : ""}`);
|
|
6288
|
+
});
|
|
6289
|
+
}
|
|
6290
|
+
if (logThisRun) {
|
|
6291
|
+
console.log(formatAstLongLines(func.node));
|
|
6292
|
+
if (copyPropStores) {
|
|
6293
|
+
copyPropStores.forEach(({ ref, ant }, node) => {
|
|
6294
|
+
console.log(`copy-prop-store: ${formatAstLongLines(node)}${ant ? "!" : ""} => ${nodeCopyProp.get(node) !== ref ? "Failed" : "Success"}`);
|
|
6295
|
+
});
|
|
6296
|
+
}
|
|
5924
6297
|
}
|
|
5925
6298
|
if (optimizeEquivalencies) {
|
|
5926
|
-
if (!nodeEquivs.size && !selfAssignments.size) {
|
|
6299
|
+
if (!nodeEquivs.size && !selfAssignments.size && !nodeCopyProp.size) {
|
|
5927
6300
|
return { istate, nodeEquivs };
|
|
5928
6301
|
}
|
|
5929
6302
|
if (logThisRun) {
|
|
@@ -5966,13 +6339,84 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
5966
6339
|
}
|
|
5967
6340
|
}
|
|
5968
6341
|
}));
|
|
5969
|
-
traverseAst(func.node.body,
|
|
6342
|
+
traverseAst(func.node.body, (node) => {
|
|
6343
|
+
if (node.type === "AssignmentExpression" &&
|
|
6344
|
+
node.operator !== "=" &&
|
|
6345
|
+
nodeCopyProp.has(node.left)) {
|
|
6346
|
+
const left = cloneDeep(node.left);
|
|
6347
|
+
const right = withLoc({
|
|
6348
|
+
type: "BinaryExpression",
|
|
6349
|
+
operator: node.operator.slice(0, -1),
|
|
6350
|
+
left: withLocDeep(node.left, node.right, false, true),
|
|
6351
|
+
right: node.right,
|
|
6352
|
+
}, node.left, node.right);
|
|
6353
|
+
node.operator = "=";
|
|
6354
|
+
node.left = left;
|
|
6355
|
+
node.right = right;
|
|
6356
|
+
}
|
|
6357
|
+
}, (node) => {
|
|
6358
|
+
const copyNode = nodeCopyProp.get(node);
|
|
6359
|
+
if (copyNode) {
|
|
6360
|
+
if (node.type === "AssignmentExpression") {
|
|
6361
|
+
if (logThisRun) {
|
|
6362
|
+
console.log(`Killing copy-prop assignment ${formatAstLongLines(node)}`);
|
|
6363
|
+
}
|
|
6364
|
+
return withLoc({ type: "Literal", value: null, raw: "null" }, node, node);
|
|
6365
|
+
}
|
|
6366
|
+
if (node.type === "VariableDeclarator") {
|
|
6367
|
+
assert(node.init);
|
|
6368
|
+
if (logThisRun) {
|
|
6369
|
+
console.log(`Killing copy-prop variable initialization ${formatAstLongLines(node)}`);
|
|
6370
|
+
}
|
|
6371
|
+
const dup = { ...node };
|
|
6372
|
+
delete dup.init;
|
|
6373
|
+
return dup;
|
|
6374
|
+
}
|
|
6375
|
+
if (copyNode.type === "AssignmentExpression") {
|
|
6376
|
+
const replacement = copyNode.operator === "="
|
|
6377
|
+
? copyNode.right
|
|
6378
|
+
: {
|
|
6379
|
+
type: "BinaryExpression",
|
|
6380
|
+
operator: copyNode.operator.slice(0, -1),
|
|
6381
|
+
left: copyNode.left,
|
|
6382
|
+
right: copyNode.right,
|
|
6383
|
+
};
|
|
6384
|
+
if (logThisRun) {
|
|
6385
|
+
console.log(`copy-prop ${formatAstLongLines(node)} => ${formatAstLongLines(replacement)}`);
|
|
6386
|
+
}
|
|
6387
|
+
return withLocDeep(replacement, node, node, false);
|
|
6388
|
+
}
|
|
6389
|
+
else if (copyNode.type === "VariableDeclarator") {
|
|
6390
|
+
assert(copyNode.init);
|
|
6391
|
+
if (logThisRun) {
|
|
6392
|
+
console.log(`copy-prop ${formatAstLongLines(node)} => ${formatAstLongLines(copyNode.init)}`);
|
|
6393
|
+
}
|
|
6394
|
+
return withLocDeep(copyNode.init, node, node, false);
|
|
6395
|
+
}
|
|
6396
|
+
assert(false);
|
|
6397
|
+
}
|
|
5970
6398
|
if (selfAssignments.has(node)) {
|
|
5971
6399
|
if (logThisRun) {
|
|
5972
6400
|
console.log(`Deleting self assignment: ${formatAst(node)} (${sourceLocation(node.loc)})`);
|
|
5973
6401
|
}
|
|
5974
6402
|
return withLoc({ type: "Literal", value: null, raw: "null" }, node, node);
|
|
5975
6403
|
}
|
|
6404
|
+
if (nodeCopyProp.size) {
|
|
6405
|
+
/*
|
|
6406
|
+
* Copy prop and equiv can interfere with each other:
|
|
6407
|
+
*
|
|
6408
|
+
* var c = g; // copy prop kills this
|
|
6409
|
+
* ...
|
|
6410
|
+
* var x = g + 2; // node equiv replaces g with c
|
|
6411
|
+
* ...
|
|
6412
|
+
* return c; // copy prop changes this to g
|
|
6413
|
+
*
|
|
6414
|
+
* So ignore equivalencies if copy prop is active.
|
|
6415
|
+
* Note that we have to re-run propagation anyway
|
|
6416
|
+
* if copy prop did anything.
|
|
6417
|
+
*/
|
|
6418
|
+
return null;
|
|
6419
|
+
}
|
|
5976
6420
|
const equiv = nodeEquivs.get(node);
|
|
5977
6421
|
if (!equiv || localConflicts.has(equiv.decl))
|
|
5978
6422
|
return null;
|
|
@@ -6024,7 +6468,11 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
6024
6468
|
return replacement;
|
|
6025
6469
|
});
|
|
6026
6470
|
}
|
|
6027
|
-
return {
|
|
6471
|
+
return {
|
|
6472
|
+
istate,
|
|
6473
|
+
nodeEquivs,
|
|
6474
|
+
redo: optimizeEquivalencies && nodeCopyProp.size,
|
|
6475
|
+
};
|
|
6028
6476
|
}
|
|
6029
6477
|
function updateByAssocPath(path, property, union) {
|
|
6030
6478
|
const valueToStore = (base) => {
|
|
@@ -6261,20 +6709,48 @@ function couldBeObj(a, b) {
|
|
|
6261
6709
|
|
|
6262
6710
|
"use strict";
|
|
6263
6711
|
/* unused harmony exports findDeadStores, eliminateDeadStores */
|
|
6264
|
-
/* harmony import */ var
|
|
6265
|
-
/* harmony import */ var
|
|
6266
|
-
/* harmony import */ var
|
|
6267
|
-
/* harmony import */ var
|
|
6268
|
-
/* harmony import */ var
|
|
6269
|
-
/* harmony import */ var
|
|
6270
|
-
/* harmony import */ var
|
|
6712
|
+
/* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4143);
|
|
6713
|
+
/* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_assert__WEBPACK_IMPORTED_MODULE_0__);
|
|
6714
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6817);
|
|
6715
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_1__);
|
|
6716
|
+
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6652);
|
|
6717
|
+
/* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5101);
|
|
6718
|
+
/* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8180);
|
|
6719
|
+
/* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(333);
|
|
6720
|
+
/* harmony import */ var _type_flow_util__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(1638);
|
|
6721
|
+
|
|
6271
6722
|
|
|
6272
6723
|
|
|
6273
6724
|
|
|
6274
6725
|
|
|
6275
6726
|
|
|
6276
6727
|
|
|
6277
|
-
function
|
|
6728
|
+
function cloneAnt(antMap) {
|
|
6729
|
+
return new Map(antMap);
|
|
6730
|
+
}
|
|
6731
|
+
function addAnt(antMap, decl, node) {
|
|
6732
|
+
assert(node.type === "Identifier");
|
|
6733
|
+
const ant = antMap.get(decl);
|
|
6734
|
+
if (ant === false || ant === node)
|
|
6735
|
+
return;
|
|
6736
|
+
if (ant === undefined) {
|
|
6737
|
+
antMap.set(decl, node);
|
|
6738
|
+
}
|
|
6739
|
+
else {
|
|
6740
|
+
antMap.set(decl, false);
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
function cloneState(blockState) {
|
|
6744
|
+
const clone = { dead: new Set(blockState.dead) };
|
|
6745
|
+
if (blockState.anticipated) {
|
|
6746
|
+
clone.anticipated = cloneAnt(blockState.anticipated);
|
|
6747
|
+
}
|
|
6748
|
+
if (blockState.partiallyAnticipated) {
|
|
6749
|
+
clone.partiallyAnticipated = cloneAnt(blockState.partiallyAnticipated);
|
|
6750
|
+
}
|
|
6751
|
+
return clone;
|
|
6752
|
+
}
|
|
6753
|
+
function findDeadStores(func, graph, nodeEquivs, findCopyPropCandidates, logThisRun) {
|
|
6278
6754
|
const order = getPostOrder(graph);
|
|
6279
6755
|
order.forEach((block, i) => {
|
|
6280
6756
|
block.order = i;
|
|
@@ -6282,13 +6758,52 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6282
6758
|
const blockStates = [];
|
|
6283
6759
|
const nodeConflicts = nodeEquivs && new Map();
|
|
6284
6760
|
const mergeStates = (to, from) => {
|
|
6285
|
-
|
|
6286
|
-
if (!from.has(decl)) {
|
|
6287
|
-
to.delete(decl);
|
|
6761
|
+
let changed = Array.from(to.dead).reduce((changed, decl) => {
|
|
6762
|
+
if (!from.dead.has(decl)) {
|
|
6763
|
+
to.dead.delete(decl);
|
|
6288
6764
|
changed = true;
|
|
6289
6765
|
}
|
|
6290
6766
|
return changed;
|
|
6291
6767
|
}, false);
|
|
6768
|
+
if (to.anticipated) {
|
|
6769
|
+
if (!from.anticipated) {
|
|
6770
|
+
delete to.anticipated;
|
|
6771
|
+
changed = true;
|
|
6772
|
+
}
|
|
6773
|
+
else {
|
|
6774
|
+
changed = Array.from(to.anticipated).reduce((changed, [decl, toant]) => {
|
|
6775
|
+
const fromant = from.anticipated.get(decl);
|
|
6776
|
+
if (toant !== fromant) {
|
|
6777
|
+
to.anticipated.delete(decl);
|
|
6778
|
+
changed = true;
|
|
6779
|
+
}
|
|
6780
|
+
return changed;
|
|
6781
|
+
}, changed);
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
if (from.partiallyAnticipated) {
|
|
6785
|
+
if (!to.partiallyAnticipated) {
|
|
6786
|
+
to.partiallyAnticipated = cloneAnt(from.partiallyAnticipated);
|
|
6787
|
+
changed = true;
|
|
6788
|
+
}
|
|
6789
|
+
else {
|
|
6790
|
+
changed = Array.from(from.partiallyAnticipated).reduce((changed, [decl, fromant]) => {
|
|
6791
|
+
const toant = to.partiallyAnticipated.get(decl);
|
|
6792
|
+
if (toant === undefined) {
|
|
6793
|
+
to.partiallyAnticipated.set(decl, fromant);
|
|
6794
|
+
changed = true;
|
|
6795
|
+
}
|
|
6796
|
+
else {
|
|
6797
|
+
if (toant !== fromant) {
|
|
6798
|
+
changed = true;
|
|
6799
|
+
to.partiallyAnticipated.set(decl, false);
|
|
6800
|
+
}
|
|
6801
|
+
}
|
|
6802
|
+
return changed;
|
|
6803
|
+
}, changed);
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
return changed;
|
|
6292
6807
|
};
|
|
6293
6808
|
const queue = new DataflowQueue();
|
|
6294
6809
|
const locals = new Set(order
|
|
@@ -6299,10 +6814,11 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6299
6814
|
: [])
|
|
6300
6815
|
.concat(func.node.params));
|
|
6301
6816
|
const deadStores = new Set();
|
|
6817
|
+
const copyPropStores = new Map();
|
|
6302
6818
|
order.forEach((block) => {
|
|
6303
6819
|
if (!block.succs) {
|
|
6304
6820
|
queue.enqueue(block);
|
|
6305
|
-
blockStates[block.order] = new Set(locals);
|
|
6821
|
+
blockStates[block.order] = { dead: new Set(locals) };
|
|
6306
6822
|
}
|
|
6307
6823
|
});
|
|
6308
6824
|
while (!queue.empty()) {
|
|
@@ -6313,10 +6829,10 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6313
6829
|
if (!blockStates[top.order]) {
|
|
6314
6830
|
throw new Error(`Block ${top.order || 0} had no state!`);
|
|
6315
6831
|
}
|
|
6316
|
-
const curState =
|
|
6832
|
+
const curState = cloneState(blockStates[top.order]);
|
|
6317
6833
|
if (logThisRun) {
|
|
6318
6834
|
printBlockHeader(top);
|
|
6319
|
-
curState.forEach((decl) => console.log(` - anticipated: ${tsKey(decl)}`));
|
|
6835
|
+
curState.dead.forEach((decl) => console.log(` - anticipated: ${tsKey(decl)}`));
|
|
6320
6836
|
}
|
|
6321
6837
|
if (top.events) {
|
|
6322
6838
|
for (let i = top.events.length; i--;) {
|
|
@@ -6331,33 +6847,63 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6331
6847
|
switch (event.type) {
|
|
6332
6848
|
case "ref":
|
|
6333
6849
|
if (isTypeStateKey(event.decl)) {
|
|
6334
|
-
curState.delete(event.decl);
|
|
6335
6850
|
if (logThisRun) {
|
|
6851
|
+
console.log(describeEvent(event));
|
|
6336
6852
|
console.log(` kill => ${tsKey(event.decl)}`);
|
|
6337
6853
|
}
|
|
6854
|
+
if (findCopyPropCandidates && declIsLocal(event.decl)) {
|
|
6855
|
+
if (!curState.anticipated) {
|
|
6856
|
+
curState.anticipated = new Map();
|
|
6857
|
+
}
|
|
6858
|
+
addAnt(curState.anticipated, event.decl, event.node);
|
|
6859
|
+
if (!curState.partiallyAnticipated) {
|
|
6860
|
+
curState.partiallyAnticipated = new Map();
|
|
6861
|
+
}
|
|
6862
|
+
addAnt(curState.partiallyAnticipated, event.decl, event.node);
|
|
6863
|
+
if (logThisRun) {
|
|
6864
|
+
console.log(` antrefs: ${curState.partiallyAnticipated.get(event.decl) !== false} ${curState.anticipated.get(event.decl) !== false}`);
|
|
6865
|
+
}
|
|
6866
|
+
}
|
|
6867
|
+
curState.dead.delete(event.decl);
|
|
6338
6868
|
}
|
|
6339
6869
|
break;
|
|
6340
6870
|
case "def":
|
|
6341
6871
|
if (isTypeStateKey(event.decl) &&
|
|
6342
6872
|
(event.node.type !== "VariableDeclarator" || event.node.init)) {
|
|
6343
|
-
if (
|
|
6873
|
+
if (logThisRun) {
|
|
6874
|
+
console.log(describeEvent(event));
|
|
6875
|
+
}
|
|
6876
|
+
const assignNode = (event.node.type === "AssignmentExpression" &&
|
|
6877
|
+
event.node.operator === "=" &&
|
|
6878
|
+
event.node.right) ||
|
|
6879
|
+
(event.node.type === "VariableDeclarator" && event.node.init);
|
|
6880
|
+
if (curState.dead.has(event.decl)) {
|
|
6344
6881
|
deadStores.add(event.node);
|
|
6345
6882
|
}
|
|
6346
6883
|
else {
|
|
6347
6884
|
deadStores.delete(event.node);
|
|
6885
|
+
copyPropStores.delete(event.node);
|
|
6886
|
+
if (declIsLocal(event.decl) && curState.partiallyAnticipated) {
|
|
6887
|
+
const pant = curState.partiallyAnticipated.get(event.decl);
|
|
6888
|
+
if (pant) {
|
|
6889
|
+
if (logThisRun) {
|
|
6890
|
+
console.log(` is copy-prop-candidate ${curState.anticipated?.get(event.decl) === pant}`);
|
|
6891
|
+
}
|
|
6892
|
+
copyPropStores.set(event.node, {
|
|
6893
|
+
ref: pant,
|
|
6894
|
+
ant: curState.anticipated?.get(event.decl) === pant,
|
|
6895
|
+
});
|
|
6896
|
+
}
|
|
6897
|
+
curState.partiallyAnticipated.delete(event.decl);
|
|
6898
|
+
curState.anticipated?.delete(event.decl);
|
|
6899
|
+
}
|
|
6348
6900
|
}
|
|
6349
6901
|
if (nodeConflicts) {
|
|
6350
6902
|
const conflicts = new Set(locals);
|
|
6351
|
-
curState.forEach((dead) => conflicts.delete(dead));
|
|
6903
|
+
curState.dead.forEach((dead) => conflicts.delete(dead));
|
|
6352
6904
|
if (event.rhs) {
|
|
6353
6905
|
conflicts.delete(event.rhs);
|
|
6354
|
-
const equiv =
|
|
6355
|
-
event.node.operator === "="
|
|
6356
|
-
? nodeEquivs.get(event.node.right)
|
|
6357
|
-
: event.node.type === "VariableDeclarator" &&
|
|
6358
|
-
event.node.init
|
|
6359
|
-
? nodeEquivs.get(event.node.init)
|
|
6360
|
-
: null;
|
|
6906
|
+
const equiv = assignNode && nodeEquivs.get(assignNode);
|
|
6361
6907
|
if (equiv) {
|
|
6362
6908
|
equiv.equiv.forEach((e) => isTypeStateKey(e) && conflicts.delete(e));
|
|
6363
6909
|
isTypeStateKey(equiv.decl) && conflicts.delete(equiv.decl);
|
|
@@ -6366,10 +6912,8 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6366
6912
|
conflicts.add(event.decl);
|
|
6367
6913
|
nodeConflicts.set(event.node, conflicts);
|
|
6368
6914
|
}
|
|
6369
|
-
if (
|
|
6370
|
-
event.
|
|
6371
|
-
(event.node.type === "VariableDeclarator" && event.node.init)) {
|
|
6372
|
-
curState.add(event.decl);
|
|
6915
|
+
if (assignNode) {
|
|
6916
|
+
curState.dead.add(event.decl);
|
|
6373
6917
|
if (logThisRun) {
|
|
6374
6918
|
console.log(` anticipated => ${tsKey(event.decl)}`);
|
|
6375
6919
|
}
|
|
@@ -6378,14 +6922,14 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6378
6922
|
break;
|
|
6379
6923
|
case "kil":
|
|
6380
6924
|
if (isTypeStateKey(event.decl)) {
|
|
6381
|
-
curState.add(event.decl);
|
|
6925
|
+
curState.dead.add(event.decl);
|
|
6382
6926
|
if (logThisRun) {
|
|
6383
6927
|
console.log(` anticipated => ${tsKey(event.decl)}`);
|
|
6384
6928
|
}
|
|
6385
6929
|
}
|
|
6386
6930
|
break;
|
|
6387
6931
|
case "mod":
|
|
6388
|
-
curState.forEach((decl) => declIsLocal(decl) || curState.delete(decl));
|
|
6932
|
+
curState.dead.forEach((decl) => declIsLocal(decl) || curState.dead.delete(decl));
|
|
6389
6933
|
break;
|
|
6390
6934
|
case "flw":
|
|
6391
6935
|
break;
|
|
@@ -6395,7 +6939,7 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6395
6939
|
const doMerge = (pred) => {
|
|
6396
6940
|
const pi = pred.order || 0;
|
|
6397
6941
|
if (!blockStates[pi]) {
|
|
6398
|
-
blockStates[pi] =
|
|
6942
|
+
blockStates[pi] = cloneState(curState);
|
|
6399
6943
|
queue.enqueue(pred);
|
|
6400
6944
|
}
|
|
6401
6945
|
else if (mergeStates(blockStates[pi], curState)) {
|
|
@@ -6427,15 +6971,18 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
|
|
|
6427
6971
|
nodeConflicts.forEach((conflicts) => conflicts.forEach((conflict) => addConflicts(conflict, conflicts)));
|
|
6428
6972
|
func.node.params.forEach((param, index, arr) => addConflicts(param, arr));
|
|
6429
6973
|
}
|
|
6430
|
-
return { deadStores, locals, localConflicts };
|
|
6974
|
+
return { deadStores, locals, localConflicts, copyPropStores };
|
|
6431
6975
|
}
|
|
6432
6976
|
function eliminateDeadStores(state, func, graph, logThisRun) {
|
|
6433
|
-
const { deadStores } = findDeadStores(func, graph, null, logThisRun);
|
|
6977
|
+
const { deadStores, copyPropStores } = findDeadStores(func, graph, null, state.config?.singleUseCopyProp ?? true, logThisRun);
|
|
6434
6978
|
if (!deadStores.size)
|
|
6435
|
-
return false;
|
|
6979
|
+
return { changes: false, copyPropStores };
|
|
6436
6980
|
if (logThisRun) {
|
|
6437
6981
|
console.log("====== Dead Stores =====");
|
|
6438
|
-
deadStores.forEach((dead) =>
|
|
6982
|
+
deadStores.forEach((dead) => (dead.type === "AssignmentExpression" ||
|
|
6983
|
+
dead.type === "UpdateExpression" ||
|
|
6984
|
+
dead.type === "VariableDeclarator") &&
|
|
6985
|
+
console.log(`${formatAst(dead)} (${sourceLocation(dead.loc)})`));
|
|
6439
6986
|
}
|
|
6440
6987
|
let changes = false;
|
|
6441
6988
|
traverseAst(func.node.body, null, (node, parent) => {
|
|
@@ -6498,7 +7045,7 @@ function eliminateDeadStores(state, func, graph, logThisRun) {
|
|
|
6498
7045
|
}
|
|
6499
7046
|
return null;
|
|
6500
7047
|
});
|
|
6501
|
-
return changes;
|
|
7048
|
+
return { copyPropStores, changes };
|
|
6502
7049
|
}
|
|
6503
7050
|
|
|
6504
7051
|
|
|
@@ -6509,9 +7056,13 @@ function eliminateDeadStores(state, func, graph, logThisRun) {
|
|
|
6509
7056
|
|
|
6510
7057
|
"use strict";
|
|
6511
7058
|
/* unused harmony exports evaluateBinaryTypes, evaluateLogicalTypes */
|
|
6512
|
-
/* harmony import */ var
|
|
6513
|
-
/* harmony import */ var
|
|
6514
|
-
/* harmony import */ var
|
|
7059
|
+
/* harmony import */ var _could_be__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4055);
|
|
7060
|
+
/* harmony import */ var _interp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7161);
|
|
7061
|
+
/* harmony import */ var _sub_type__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9234);
|
|
7062
|
+
/* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7255);
|
|
7063
|
+
/* harmony import */ var _union_type__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(757);
|
|
7064
|
+
|
|
7065
|
+
|
|
6515
7066
|
|
|
6516
7067
|
|
|
6517
7068
|
|
|
@@ -6842,14 +7393,6 @@ function evaluateBinaryTypes(op, left, right) {
|
|
|
6842
7393
|
: 2 /* TypeTag.False */,
|
|
6843
7394
|
}),
|
|
6844
7395
|
},
|
|
6845
|
-
instanceof: {
|
|
6846
|
-
allowed: 524287 /* TypeTag.Any */,
|
|
6847
|
-
typeFn: () => ({
|
|
6848
|
-
tag: 6 /* TypeTag.Boolean */,
|
|
6849
|
-
castArgs: false,
|
|
6850
|
-
}),
|
|
6851
|
-
valueFn: (_left, _right) => undefined,
|
|
6852
|
-
},
|
|
6853
7396
|
has: {
|
|
6854
7397
|
allowed: 524287 /* TypeTag.Any */,
|
|
6855
7398
|
typeFn: () => ({
|
|
@@ -6860,6 +7403,21 @@ function evaluateBinaryTypes(op, left, right) {
|
|
|
6860
7403
|
},
|
|
6861
7404
|
};
|
|
6862
7405
|
}
|
|
7406
|
+
if (op === "instanceof") {
|
|
7407
|
+
if (right.type & 16384 /* TypeTag.Class */) {
|
|
7408
|
+
if (!isExact(right)) {
|
|
7409
|
+
return { type: 6 /* TypeTag.Boolean */ };
|
|
7410
|
+
}
|
|
7411
|
+
right = { type: 32768 /* TypeTag.Object */, value: { klass: right } };
|
|
7412
|
+
}
|
|
7413
|
+
return {
|
|
7414
|
+
type: subtypeOf(left, right)
|
|
7415
|
+
? 4 /* TypeTag.True */
|
|
7416
|
+
: !couldBeWeak(left, right)
|
|
7417
|
+
? 2 /* TypeTag.False */
|
|
7418
|
+
: 6 /* TypeTag.Boolean */,
|
|
7419
|
+
};
|
|
7420
|
+
}
|
|
6863
7421
|
const info = operators[op];
|
|
6864
7422
|
if (!info)
|
|
6865
7423
|
return { type: 524287 /* TypeTag.Any */ };
|
|
@@ -6982,7 +7540,7 @@ function checkCallArgs(istate, node, callees, args) {
|
|
|
6982
7540
|
let argEffects = true;
|
|
6983
7541
|
const object = calleeObjectType(istate, node.callee);
|
|
6984
7542
|
if (object) {
|
|
6985
|
-
const info = sysCallInfo(cur);
|
|
7543
|
+
const info = sysCallInfo(istate.state, cur);
|
|
6986
7544
|
if (info) {
|
|
6987
7545
|
const result = info(istate.state, cur, object, () => args);
|
|
6988
7546
|
if (result.argTypes)
|
|
@@ -7113,16 +7671,18 @@ function isOverride(cur, funcs) {
|
|
|
7113
7671
|
return false;
|
|
7114
7672
|
}
|
|
7115
7673
|
let systemCallInfo = null;
|
|
7116
|
-
|
|
7117
|
-
|
|
7674
|
+
let systemCallVersion;
|
|
7675
|
+
function sysCallInfo(state, func) {
|
|
7676
|
+
const info = getSystemCallTable(state);
|
|
7118
7677
|
if (hasProperty(info, func.fullName)) {
|
|
7119
7678
|
return info[func.fullName];
|
|
7120
7679
|
}
|
|
7121
7680
|
return null;
|
|
7122
7681
|
}
|
|
7123
|
-
function getSystemCallTable() {
|
|
7124
|
-
if (systemCallInfo)
|
|
7682
|
+
function getSystemCallTable(state) {
|
|
7683
|
+
if (systemCallInfo && systemCallVersion === state.sdk) {
|
|
7125
7684
|
return systemCallInfo;
|
|
7685
|
+
}
|
|
7126
7686
|
const arrayAdd = (state, callee, calleeObj, getArgs) => {
|
|
7127
7687
|
const ret = {};
|
|
7128
7688
|
if (calleeObj.type & 512 /* TypeTag.Array */) {
|
|
@@ -7342,7 +7902,8 @@ function getSystemCallTable() {
|
|
|
7342
7902
|
}
|
|
7343
7903
|
return results;
|
|
7344
7904
|
};
|
|
7345
|
-
|
|
7905
|
+
systemCallVersion = state.sdk;
|
|
7906
|
+
return (systemCallInfo = expandKeys(state, {
|
|
7346
7907
|
"$.Toybox.Lang.Array.add": arrayAdd,
|
|
7347
7908
|
"$.Toybox.Lang.Array.addAll": arrayAdd,
|
|
7348
7909
|
"$.Toybox.Lang.Array.remove": mod,
|
|
@@ -7385,7 +7946,47 @@ function getSystemCallTable() {
|
|
|
7385
7946
|
"$.Toybox.Math.variance": nop,
|
|
7386
7947
|
"$.Toybox.Math.srand": mod,
|
|
7387
7948
|
"$.Toybox.Math.rand": mod,
|
|
7949
|
+
"$.Toybox.Lang.*.(to*|equals|abs)": nop,
|
|
7950
|
+
"$.Toybox.Time.Gregorian.(duration|info|localMoment|moment|utcInfo)": nop,
|
|
7951
|
+
"$.Toybox.Time.(Duration|LocalMoment|Moment).(?!initialize)*": nop,
|
|
7952
|
+
"$.Toybox.Graphics.Dc.get*": nop,
|
|
7953
|
+
}));
|
|
7954
|
+
}
|
|
7955
|
+
function expandKeys(state, table) {
|
|
7956
|
+
const result = {};
|
|
7957
|
+
const pattern = /[*()|]/;
|
|
7958
|
+
Object.entries(table).forEach(([key, value]) => {
|
|
7959
|
+
if (!pattern.test(key)) {
|
|
7960
|
+
result[key] = value;
|
|
7961
|
+
return;
|
|
7962
|
+
}
|
|
7963
|
+
if (state.stack) {
|
|
7964
|
+
const decls = key
|
|
7965
|
+
.split(".")
|
|
7966
|
+
.slice(1)
|
|
7967
|
+
.reduce((decls, decl) => {
|
|
7968
|
+
if (pattern.test(decl)) {
|
|
7969
|
+
const re = new RegExp(`^${decl.replace(/\*/g, ".*")}$`);
|
|
7970
|
+
return decls.flatMap((sn) => isStateNode(sn) && sn.decls
|
|
7971
|
+
? Object.keys(sn.decls)
|
|
7972
|
+
.filter((m) => re.test(m))
|
|
7973
|
+
.flatMap((m) => sn.decls[m])
|
|
7974
|
+
: []);
|
|
7975
|
+
}
|
|
7976
|
+
else {
|
|
7977
|
+
return decls.flatMap((sn) => (isStateNode(sn) && sn.decls?.[decl]) || []);
|
|
7978
|
+
}
|
|
7979
|
+
}, [state.stack[0].sn]);
|
|
7980
|
+
decls.forEach((decl) => {
|
|
7981
|
+
if (decl.type === "FunctionDeclaration") {
|
|
7982
|
+
if (!hasProperty(result, decl.fullName)) {
|
|
7983
|
+
result[decl.fullName] = value;
|
|
7984
|
+
}
|
|
7985
|
+
}
|
|
7986
|
+
});
|
|
7987
|
+
}
|
|
7388
7988
|
});
|
|
7989
|
+
return result;
|
|
7389
7990
|
}
|
|
7390
7991
|
|
|
7391
7992
|
|
|
@@ -9064,6 +9665,24 @@ function optimizeFunction(state, func) {
|
|
|
9064
9665
|
}
|
|
9065
9666
|
function beforeEvaluate(istate, node) {
|
|
9066
9667
|
switch (node.type) {
|
|
9668
|
+
case "ExpressionStatement": {
|
|
9669
|
+
if (node.expression.type !== "Literal") {
|
|
9670
|
+
const expression = popIstate(istate, node.expression);
|
|
9671
|
+
if (expression.embeddedEffects) {
|
|
9672
|
+
istate.stack.push(expression);
|
|
9673
|
+
}
|
|
9674
|
+
else {
|
|
9675
|
+
const rep = withLoc({ type: "Literal", value: null, raw: "null" }, node, node);
|
|
9676
|
+
istate.stack.push({
|
|
9677
|
+
value: { type: 1 /* TypeTag.Null */ },
|
|
9678
|
+
embeddedEffects: false,
|
|
9679
|
+
node: rep,
|
|
9680
|
+
});
|
|
9681
|
+
node.expression = rep;
|
|
9682
|
+
}
|
|
9683
|
+
}
|
|
9684
|
+
break;
|
|
9685
|
+
}
|
|
9067
9686
|
case "ConditionalExpression": {
|
|
9068
9687
|
let alternate = tryPop(istate, node.alternate);
|
|
9069
9688
|
let consequent = tryPop(istate, node.consequent);
|
|
@@ -9812,7 +10431,7 @@ function subtypeOfObj(a, b) {
|
|
|
9812
10431
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9813
10432
|
/* harmony export */ "nK": () => (/* binding */ findObjectDeclsByProperty)
|
|
9814
10433
|
/* harmony export */ });
|
|
9815
|
-
/* unused harmony exports isTypeStateKey, declIsLocal, localDeclName, tsKey, sourceLocation, printBlockHeader, describeEvent, printBlockEvents, printBlockTrailer, refineObjectTypeByDecls, findNextObjectType, resolveDottedMember */
|
|
10434
|
+
/* unused harmony exports isTypeStateKey, declIsLocal, declIsNonLocal, localDeclName, tsKey, sourceLocation, printBlockHeader, describeEvent, printBlockEvents, printBlockTrailer, refineObjectTypeByDecls, findNextObjectType, resolveDottedMember */
|
|
9816
10435
|
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
|
|
9817
10436
|
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
|
|
9818
10437
|
/* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8180);
|
|
@@ -9838,6 +10457,9 @@ function declIsLocal(decl) {
|
|
|
9838
10457
|
d.type === "Identifier" ||
|
|
9839
10458
|
(d.type === "VariableDeclarator" && isLocal(d)));
|
|
9840
10459
|
}
|
|
10460
|
+
function declIsNonLocal(decl) {
|
|
10461
|
+
return some(decl, (d) => d.type === "VariableDeclarator" && !isLocal(d));
|
|
10462
|
+
}
|
|
9841
10463
|
function localDeclName(decl) {
|
|
9842
10464
|
if (Array.isArray(decl))
|
|
9843
10465
|
decl = decl[0];
|
|
@@ -11300,6 +11922,23 @@ function cleanupUnusedVars(state, node) {
|
|
|
11300
11922
|
return false;
|
|
11301
11923
|
const varDeclarations = new Map();
|
|
11302
11924
|
const stack = [];
|
|
11925
|
+
/*
|
|
11926
|
+
* Every local mentioned in toRemove can be removed, but
|
|
11927
|
+
* occurances of the identifier prior to its declaration
|
|
11928
|
+
* must be non-local. So reconstruct the toRemove record
|
|
11929
|
+
* as we go. This is to prevent issues with something like
|
|
11930
|
+
*
|
|
11931
|
+
* var g = 0;
|
|
11932
|
+
* function foo(var h) {
|
|
11933
|
+
* // if we just consult toRemove, we'll remove this assignment
|
|
11934
|
+
* g = h * 2;
|
|
11935
|
+
* var x = g;
|
|
11936
|
+
* ...
|
|
11937
|
+
* // *This* g is unused, and "g" gets added to activeRemove here.
|
|
11938
|
+
* var g = 42;
|
|
11939
|
+
* }
|
|
11940
|
+
*/
|
|
11941
|
+
const activeRemove = {};
|
|
11303
11942
|
let changes = false;
|
|
11304
11943
|
traverseAst(node, (node) => {
|
|
11305
11944
|
switch (node.type) {
|
|
@@ -11320,6 +11959,7 @@ function cleanupUnusedVars(state, node) {
|
|
|
11320
11959
|
node.declarations.forEach((decl, i) => {
|
|
11321
11960
|
const name = variableDeclarationName(decl.id);
|
|
11322
11961
|
if (hasProperty(toRemove, name)) {
|
|
11962
|
+
activeRemove[name] = toRemove[name];
|
|
11323
11963
|
const info = varDeclarations.get(node);
|
|
11324
11964
|
if (info) {
|
|
11325
11965
|
info.indices.push(i);
|
|
@@ -11337,14 +11977,14 @@ function cleanupUnusedVars(state, node) {
|
|
|
11337
11977
|
case "ExpressionStatement":
|
|
11338
11978
|
if (node.expression.type === "AssignmentExpression") {
|
|
11339
11979
|
if (node.expression.left.type === "Identifier" &&
|
|
11340
|
-
hasProperty(
|
|
11980
|
+
hasProperty(activeRemove, node.expression.left.name)) {
|
|
11341
11981
|
changes = true;
|
|
11342
11982
|
return unused(state, node.expression.right);
|
|
11343
11983
|
}
|
|
11344
11984
|
}
|
|
11345
11985
|
else if (node.expression.type === "UpdateExpression" &&
|
|
11346
11986
|
node.expression.argument.type === "Identifier" &&
|
|
11347
|
-
hasProperty(
|
|
11987
|
+
hasProperty(activeRemove, node.expression.argument.name)) {
|
|
11348
11988
|
return false;
|
|
11349
11989
|
}
|
|
11350
11990
|
break;
|
|
@@ -11353,7 +11993,7 @@ function cleanupUnusedVars(state, node) {
|
|
|
11353
11993
|
const expr = node.expressions[i];
|
|
11354
11994
|
if (expr.type === "AssignmentExpression") {
|
|
11355
11995
|
if (expr.left.type === "Identifier" &&
|
|
11356
|
-
hasProperty(
|
|
11996
|
+
hasProperty(activeRemove, expr.left.name)) {
|
|
11357
11997
|
const rep = unused(state, expr.right);
|
|
11358
11998
|
if (!rep.length) {
|
|
11359
11999
|
changes = true;
|
|
@@ -11362,14 +12002,14 @@ function cleanupUnusedVars(state, node) {
|
|
|
11362
12002
|
else {
|
|
11363
12003
|
// Sequence expressions can only be assignments
|
|
11364
12004
|
// or update expressions. Even calls aren't allowed
|
|
11365
|
-
|
|
12005
|
+
activeRemove[expr.left.name] = null;
|
|
11366
12006
|
expr.operator = "=";
|
|
11367
12007
|
}
|
|
11368
12008
|
}
|
|
11369
12009
|
}
|
|
11370
12010
|
else if (expr.type === "UpdateExpression" &&
|
|
11371
12011
|
expr.argument.type === "Identifier" &&
|
|
11372
|
-
hasProperty(
|
|
12012
|
+
hasProperty(activeRemove, expr.argument.name)) {
|
|
11373
12013
|
changes = true;
|
|
11374
12014
|
node.expressions.splice(i, 1);
|
|
11375
12015
|
}
|
|
@@ -11385,7 +12025,7 @@ function cleanupUnusedVars(state, node) {
|
|
|
11385
12025
|
const i = info.indices[ii];
|
|
11386
12026
|
const vdecl = decl.declarations[i];
|
|
11387
12027
|
const name = variableDeclarationName(vdecl.id);
|
|
11388
|
-
if (hasProperty(
|
|
12028
|
+
if (hasProperty(activeRemove, name)) {
|
|
11389
12029
|
const rep = vdecl.init ? unused(state, vdecl.init) : [];
|
|
11390
12030
|
if (rep.length) {
|
|
11391
12031
|
if ((state.sdkVersion || 0) < 4001007 &&
|
|
@@ -11430,7 +12070,7 @@ function cleanupUnusedVars(state, node) {
|
|
|
11430
12070
|
j = i;
|
|
11431
12071
|
continue;
|
|
11432
12072
|
}
|
|
11433
|
-
if (
|
|
12073
|
+
if (activeRemove[name]) {
|
|
11434
12074
|
changes = true;
|
|
11435
12075
|
j--;
|
|
11436
12076
|
decl.declarations.splice(i, 1);
|
|
@@ -13392,13 +14032,14 @@ async function createDocumentationMap(functionDocumentation) {
|
|
|
13392
14032
|
state.allFunctions[info.name]?.forEach((decl) => decl.node?.loc?.source === "api.mir" &&
|
|
13393
14033
|
decl.fullName.endsWith(`.${info.parent}.${info.name}`) &&
|
|
13394
14034
|
docMap.set(decl.fullName, info.doc
|
|
14035
|
+
.replace(/@example\s*(.*?)<br\/>(.*?)(@|$)/g, (match, title, m1, m2) => `\n#### Example: ${title}\n\`\`\`${m1.replace(/<br\/>/g, "\n")}\`\`\`${m2}`)
|
|
13395
14036
|
.replace(/(\*.*?)\s*<br\/>\s*(?!\s*\*)/g, "$1\n\n")
|
|
13396
|
-
.replace(/@example(.*?)(@|$)/g, (match, m1, m2) => `\n#### Example\n\`\`\`${m1.replace(/<br\/>/g, "\n")}\`\`\`${m2}`)
|
|
13397
14037
|
.replace(/@note/g, "\n#### Note\n")
|
|
13398
14038
|
.replace(/@see/, "\n#### See Also:\n$&")
|
|
13399
14039
|
.replace(/@see\s+(.*?)(?=<br\/>)/g, "\n * {$1}")
|
|
13400
14040
|
.replace(/@throws/, "\n#### Throws:\n$&")
|
|
13401
14041
|
.replace(/@throws\s+(.*?)(?=<br\/>)/g, "\n * $1")
|
|
14042
|
+
.replace(/@option\s+\w+\s+(.*?)(?=<br\/>)/g, "\n - $1")
|
|
13402
14043
|
.replace(/@since\s+(.*?)(?=<br\/>)/, "\n#### Since:\nAPI Level $1\n")
|
|
13403
14044
|
.replace(/<div class="description">/, "### Description\n")
|
|
13404
14045
|
.replace(/<div class="param">/, "\n#### Parameters\n$&")
|