@suss/resolution 0.34.1 → 0.35.1
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/dist/index.cjs +566 -13
- package/dist/index.d.cts +85 -1
- package/dist/index.d.ts +85 -1
- package/dist/index.js +558 -13
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ var ASKING_RELATIONS = [
|
|
|
17
17
|
"wantedEnvObject",
|
|
18
18
|
"wantedStreamObject",
|
|
19
19
|
"wantedExitSink",
|
|
20
|
+
"wantedDispatch",
|
|
20
21
|
"wantedOrigin",
|
|
21
22
|
"wantedCallOrigin",
|
|
22
23
|
"wantedExportsOf",
|
|
@@ -534,6 +535,29 @@ function classMemberOf(name) {
|
|
|
534
535
|
return name.startsWith(CLASS_MEMBER_PREFIX) ? name.slice(CLASS_MEMBER_PREFIX.length) : null;
|
|
535
536
|
}
|
|
536
537
|
|
|
538
|
+
// src/valuePositions.ts
|
|
539
|
+
var VALUE_ENTRY = "value";
|
|
540
|
+
var SPREAD_ENTRY = "spread";
|
|
541
|
+
function addEntryRows(add, container, entries) {
|
|
542
|
+
entries.forEach((entry, position) => {
|
|
543
|
+
add("writtenEntryAt", [container, String(position), entry.key, entry.form]);
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
function addTemplateRows(add, template, pieces) {
|
|
547
|
+
let position = 0;
|
|
548
|
+
for (const piece of pieces) {
|
|
549
|
+
if ("hole" in piece) {
|
|
550
|
+
add("holeAt", [template, String(position), piece.hole]);
|
|
551
|
+
position += 1;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
if (piece.text !== "") {
|
|
555
|
+
add("partAt", [template, String(position), piece.text]);
|
|
556
|
+
position += 1;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
537
561
|
// src/contract.ts
|
|
538
562
|
var distinct = (values) => new Set(values).size;
|
|
539
563
|
function leadsToOneOf(facts, key, targets) {
|
|
@@ -551,6 +575,30 @@ function leadsToOneOf(facts, key, targets) {
|
|
|
551
575
|
return false;
|
|
552
576
|
}
|
|
553
577
|
var RELATIONS_THAT_CAN_CROSS = ["imports", "binds", "reExports"];
|
|
578
|
+
function someValueLeadsTo(facts, key) {
|
|
579
|
+
const target = /* @__PURE__ */ new Set([key]);
|
|
580
|
+
return ["returnsValue", "holdsUnderKey"].some(
|
|
581
|
+
(relation) => facts(relation).some((row) => leadsToOneOf(facts, row[1] ?? "", target))
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
function holdsValueThenSpread(facts, name) {
|
|
585
|
+
const entries = facts("writtenEntryAt");
|
|
586
|
+
const held = facts("holdsProperty");
|
|
587
|
+
return [...new Set(entries.map((row) => row[0]))].some((container) => {
|
|
588
|
+
const own = entries.filter((row) => row[0] === container);
|
|
589
|
+
const value = own.find((row) => row[1] === "0");
|
|
590
|
+
const spread2 = own.find((row) => row[1] === "1");
|
|
591
|
+
return own.length === 2 && value?.[3] === VALUE_ENTRY && spread2?.[3] === SPREAD_ENTRY && held.some(
|
|
592
|
+
(row) => row[0] === container && row[1] === name && row[2] === value[2]
|
|
593
|
+
) && !held.some((row) => row[0] === container && row[2] === spread2[2]);
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
function valueThenSpread(facts, name) {
|
|
597
|
+
if (facts("writtenEntryAt").length === 0) {
|
|
598
|
+
return "no list or object states its entries, so nothing says the order they are written in";
|
|
599
|
+
}
|
|
600
|
+
return holdsValueThenSpread(facts, name) ? null : `no container's entries are the value under ${name} at 0, keyed as holdsProperty keys it, and then a spread at 1 keyed on the collection it unpacks`;
|
|
601
|
+
}
|
|
554
602
|
var FACT_CONTRACT_CASES = [
|
|
555
603
|
{
|
|
556
604
|
name: "two functions, one parameter name",
|
|
@@ -749,6 +797,122 @@ var FACT_CONTRACT_CASES = [
|
|
|
749
797
|
return stores.some((row) => leadsToOneOf(facts, row[0] ?? "", sites)) ? null : "the write is keyed so that nothing leads from it to the construction, so the rules cannot find the object it lands on";
|
|
750
798
|
}
|
|
751
799
|
},
|
|
800
|
+
{
|
|
801
|
+
name: "a table of functions called by a key worked out at run time",
|
|
802
|
+
requires: "a module-level table written out with a function under the name created and another under deleted, and a function that calls the table's entry under its parameter, `TABLE[kind](payload)` or the language's own spelling",
|
|
803
|
+
// The rules take the entries off the object the read's container
|
|
804
|
+
// leads to, so the container has to be keyed the way `binds` keys it,
|
|
805
|
+
// and each entry under the name the source writes, without quotes.
|
|
806
|
+
check: (facts) => {
|
|
807
|
+
const entries = facts("holdsProperty");
|
|
808
|
+
const tables = new Set(
|
|
809
|
+
entries.filter((row) => row[1] === "created").map((row) => row[0] ?? "").filter(
|
|
810
|
+
(table) => entries.some((row) => row[0] === table && row[1] === "deleted")
|
|
811
|
+
)
|
|
812
|
+
);
|
|
813
|
+
if (tables.size === 0) {
|
|
814
|
+
return "no object contains a function under created and another under deleted, written without quotes";
|
|
815
|
+
}
|
|
816
|
+
const reads = facts("choosesEntry");
|
|
817
|
+
if (reads.length === 0) {
|
|
818
|
+
return "the read at a key worked out at run time is not written down as choosesEntry, so no call through it finds the table's functions";
|
|
819
|
+
}
|
|
820
|
+
return reads.some((row) => leadsToOneOf(facts, row[1] ?? "", tables)) ? null : "the read's container is keyed so that nothing leads from it to the table";
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
name: "a function written into a table under a computed key",
|
|
825
|
+
requires: "a module-level empty table, and a function that writes its second parameter into it under its first, `REGISTRY[name] = handler` or the language's own spelling",
|
|
826
|
+
// A registry fills its table from a function its callers pass handlers
|
|
827
|
+
// to, so the write has to say the parameter went in, keyed so that
|
|
828
|
+
// `binds` leads from where it went to the table.
|
|
829
|
+
check: (facts) => {
|
|
830
|
+
const params = new Set(facts("paramOf").map((row) => row[2] ?? ""));
|
|
831
|
+
const writes = facts("holdsUnderKey").filter(
|
|
832
|
+
(row) => leadsToOneOf(facts, row[1] ?? "", params)
|
|
833
|
+
);
|
|
834
|
+
if (writes.length === 0) {
|
|
835
|
+
return "the write of a parameter into a table under a computed key is not written down as holdsUnderKey";
|
|
836
|
+
}
|
|
837
|
+
const objects = new Set(facts("objectValue").map((row) => row[0] ?? ""));
|
|
838
|
+
return writes.some((row) => leadsToOneOf(facts, row[0] ?? "", objects)) ? null : "the write is keyed so that nothing leads from it to the table";
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
name: "a list written with a spread",
|
|
843
|
+
requires: "a module-level name assigned a written-out list of a name and then a spread of another name, `[first, ...rest]` or the language's own spelling",
|
|
844
|
+
check: (facts) => valueThenSpread(facts, "0")
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
name: "an object written with a spread",
|
|
848
|
+
requires: "a module-level name assigned an object or a dictionary written out with a name under the key a, written as a string where the language writes keys that way, and then a spread of another name",
|
|
849
|
+
check: (facts) => valueThenSpread(facts, "a")
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
name: "a template string with a hole",
|
|
853
|
+
requires: "a module-level name assigned the result of a call, and a template string that puts that name between two pieces of text, `/users/${id}/posts` or the language's own spelling",
|
|
854
|
+
check: (facts) => {
|
|
855
|
+
const parts = facts("partAt");
|
|
856
|
+
const [hole, ...moreHoles] = facts("holeAt");
|
|
857
|
+
if (parts.length !== 2 || hole === void 0 || moreHoles.length > 0) {
|
|
858
|
+
return `expected two parts and one hole, found ${parts.length} and ${moreHoles.length + (hole === void 0 ? 0 : 1)}`;
|
|
859
|
+
}
|
|
860
|
+
const positions = [...parts, hole].filter((row) => row[0] === hole[0]).map((row) => row[1]).sort();
|
|
861
|
+
if (positions.join() !== "0,1,2" || hole[1] !== "1") {
|
|
862
|
+
return "the parts and the hole of one template are not numbered together in source order from 0";
|
|
863
|
+
}
|
|
864
|
+
const calls = new Set(facts("call").map((row) => row[0] ?? ""));
|
|
865
|
+
return leadsToOneOf(facts, hole[2] ?? "", calls) ? null : "the hole is keyed so that nothing leads from it to the value the name is given";
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
name: "a callback given each element of a list",
|
|
870
|
+
requires: "a function that maps its parameter through a callback returning the element it is given, `users.map((user) => user)`, a comprehension or the language's own spelling",
|
|
871
|
+
// The collection is a read of the function's parameter, and the
|
|
872
|
+
// callback's own read of its variable joins the key elementOf states.
|
|
873
|
+
check: (facts) => {
|
|
874
|
+
const [row, ...more] = facts("elementOf");
|
|
875
|
+
if (row === void 0 || more.length > 0) {
|
|
876
|
+
return `expected one elementOf row, found ${more.length + (row === void 0 ? 0 : 1)}`;
|
|
877
|
+
}
|
|
878
|
+
const params = new Set(facts("paramOf").map((one) => one[2] ?? ""));
|
|
879
|
+
if (!leadsToOneOf(facts, row[1] ?? "", params)) {
|
|
880
|
+
return "the collection is keyed so that nothing leads from it to the parameter it reads";
|
|
881
|
+
}
|
|
882
|
+
return someValueLeadsTo(facts, row[0] ?? "") ? null : "no read of the variable leads to the key elementOf gives it, so nothing knows what the read is given";
|
|
883
|
+
}
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
name: "a callback given the parts of each element of a list",
|
|
887
|
+
requires: "a function that maps its parameter through a callback taking each element apart into two names and returning the first, `pairs.map(([name, kind]) => name)`, `[name for name, kind in pairs]` or the language's own spelling",
|
|
888
|
+
check: (facts) => {
|
|
889
|
+
const parts = facts("elementPartOf");
|
|
890
|
+
const first = parts.find((row) => row[2] === "0");
|
|
891
|
+
const second = parts.find((row) => row[2] === "1");
|
|
892
|
+
if (first === void 0 || second === void 0) {
|
|
893
|
+
return "no two names are given parts 0 and 1 of each element, so nothing says what a name taken apart is given";
|
|
894
|
+
}
|
|
895
|
+
const params = new Set(facts("paramOf").map((one) => one[2] ?? ""));
|
|
896
|
+
if (first[1] !== second[1] || !leadsToOneOf(facts, first[1] ?? "", params)) {
|
|
897
|
+
return "the two parts name different collections, or one that nothing leads from to the parameter it reads";
|
|
898
|
+
}
|
|
899
|
+
return someValueLeadsTo(facts, first[0] ?? "") ? null : "no read of the first name leads to the key elementPartOf gives it";
|
|
900
|
+
}
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
name: "a callback given each position in a list",
|
|
904
|
+
requires: "a function that maps its parameter through a callback taking the element and then its position and returning the position, `users.map((user, at) => at)`, `enumerate` in a comprehension, or the language's own spelling",
|
|
905
|
+
check: (facts) => {
|
|
906
|
+
const [row, ...more] = facts("positionOf");
|
|
907
|
+
if (row === void 0 || more.length > 0) {
|
|
908
|
+
return `expected one positionOf row, found ${more.length + (row === void 0 ? 0 : 1)}`;
|
|
909
|
+
}
|
|
910
|
+
if (!facts("elementOf").some((one) => one[0] === row[1])) {
|
|
911
|
+
return "the position is tied to a variable that no elementOf row gives an element, so nothing says what it counts through";
|
|
912
|
+
}
|
|
913
|
+
return someValueLeadsTo(facts, row[0] ?? "") ? null : "no read of the position leads to the key positionOf gives it";
|
|
914
|
+
}
|
|
915
|
+
},
|
|
752
916
|
{
|
|
753
917
|
name: "a value another file declares",
|
|
754
918
|
requires: "two files, one declaring a value and the other reading it by name",
|
|
@@ -1151,13 +1315,13 @@ var FactLog = class {
|
|
|
1151
1315
|
hashesByKey(relation, row) {
|
|
1152
1316
|
const hashes = /* @__PURE__ */ new Map();
|
|
1153
1317
|
const atoms = row.map((atom) => this.hashesOf(String(atom)));
|
|
1154
|
-
const term = (hash,
|
|
1318
|
+
const term = (hash, column2) => spread(hash + Math.imul(column2 + 1, 2654435761) >>> 0);
|
|
1155
1319
|
let loose = hashString(relation);
|
|
1156
|
-
atoms.forEach((atom,
|
|
1157
|
-
loose = loose + term(atom.loose,
|
|
1320
|
+
atoms.forEach((atom, column2) => {
|
|
1321
|
+
loose = loose + term(atom.loose, column2) >>> 0;
|
|
1158
1322
|
});
|
|
1159
|
-
atoms.forEach((atom,
|
|
1160
|
-
const key = String(row[
|
|
1323
|
+
atoms.forEach((atom, column2) => {
|
|
1324
|
+
const key = String(row[column2]);
|
|
1161
1325
|
if (!atom.key || hashes.has(key)) {
|
|
1162
1326
|
return;
|
|
1163
1327
|
}
|
|
@@ -1254,6 +1418,107 @@ var FactLog = class {
|
|
|
1254
1418
|
}
|
|
1255
1419
|
};
|
|
1256
1420
|
|
|
1421
|
+
// src/dispatch.ts
|
|
1422
|
+
function dispatchOutcomes(db, calls, program = resolutionProgram()) {
|
|
1423
|
+
const outcomes = /* @__PURE__ */ new Map();
|
|
1424
|
+
if (calls.length === 0) {
|
|
1425
|
+
return outcomes;
|
|
1426
|
+
}
|
|
1427
|
+
askResolution(db, calls, DISPATCH_QUESTION, program);
|
|
1428
|
+
return readDispatchOutcomes(db, calls);
|
|
1429
|
+
}
|
|
1430
|
+
var DISPATCH_QUESTION = "wantedDispatch";
|
|
1431
|
+
function readDispatchOutcomes(db, calls) {
|
|
1432
|
+
const outcomes = /* @__PURE__ */ new Map();
|
|
1433
|
+
for (const call of new Set(calls)) {
|
|
1434
|
+
const sites = column(db, "wantedDispatchSite", call, 1);
|
|
1435
|
+
if (sites.length === 0) {
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
outcomes.set(call, outcomeOf(db, call, sites));
|
|
1439
|
+
}
|
|
1440
|
+
return outcomes;
|
|
1441
|
+
}
|
|
1442
|
+
function outcomeOf(db, call, sites) {
|
|
1443
|
+
const byEntry = /* @__PURE__ */ new Map();
|
|
1444
|
+
for (const entry of column(db, "wantedDispatchEntry", call, 1)) {
|
|
1445
|
+
byEntry.set(entry, /* @__PURE__ */ new Set());
|
|
1446
|
+
}
|
|
1447
|
+
for (const row of db.lookup("wantedDispatchLikeEntry", 0, call)) {
|
|
1448
|
+
const held = String(row[3]);
|
|
1449
|
+
if (!byEntry.has(held) && matchesWrittenPart(db, String(row[1]), String(row[2]))) {
|
|
1450
|
+
byEntry.set(held, /* @__PURE__ */ new Set());
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
for (const relation of ENTRY_FUNCTIONS) {
|
|
1454
|
+
for (const row of db.lookup(relation, 0, call)) {
|
|
1455
|
+
byEntry.get(String(row[1]))?.add(String(row[2]));
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
const functions = [
|
|
1459
|
+
...new Set([...byEntry.values()].flatMap((found) => [...found]))
|
|
1460
|
+
];
|
|
1461
|
+
const unresolved = [...byEntry.values()].filter(
|
|
1462
|
+
(found) => found.size === 0
|
|
1463
|
+
).length;
|
|
1464
|
+
const through = /* @__PURE__ */ new Map();
|
|
1465
|
+
for (const row of db.lookup("wantedDispatchThrough", 0, call)) {
|
|
1466
|
+
const inner = through.get(String(row[1])) ?? [];
|
|
1467
|
+
inner.push(String(row[2]));
|
|
1468
|
+
through.set(String(row[1]), inner);
|
|
1469
|
+
}
|
|
1470
|
+
return {
|
|
1471
|
+
functions,
|
|
1472
|
+
certain: functions.length === 1 && unresolved === 0 && narrowedToOneName(db, sites) && column(db, "wantedDispatchComputed", call, 1).length === 0 && column(db, "wantedDispatchInBranch", call, 1).length === 0,
|
|
1473
|
+
unresolved,
|
|
1474
|
+
through,
|
|
1475
|
+
containers: [
|
|
1476
|
+
...new Set(
|
|
1477
|
+
sites.flatMap(
|
|
1478
|
+
(site) => ENTRY_READS.flatMap((relation) => column(db, relation, site, 1))
|
|
1479
|
+
)
|
|
1480
|
+
)
|
|
1481
|
+
]
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
var ENTRY_READS = [
|
|
1485
|
+
"choosesEntry",
|
|
1486
|
+
"choosesNamedEntry",
|
|
1487
|
+
"choosesEntryLike",
|
|
1488
|
+
"readsEntry"
|
|
1489
|
+
];
|
|
1490
|
+
var ENTRY_FUNCTIONS = [
|
|
1491
|
+
"wantedDispatchFunction",
|
|
1492
|
+
"wantedDispatchLikeFunction"
|
|
1493
|
+
];
|
|
1494
|
+
function matchesWrittenPart(db, site, name) {
|
|
1495
|
+
return db.lookup("choosesEntryLike", 0, site).some((row) => {
|
|
1496
|
+
const prefix = String(row[2]);
|
|
1497
|
+
const suffix = String(row[3]);
|
|
1498
|
+
return name.length >= prefix.length + suffix.length && name.startsWith(prefix) && name.endsWith(suffix);
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
function narrowedToOneName(db, sites) {
|
|
1502
|
+
const names = /* @__PURE__ */ new Set();
|
|
1503
|
+
for (const site of sites) {
|
|
1504
|
+
if (db.lookup("choosesEntry", 0, site).length > 0) {
|
|
1505
|
+
return false;
|
|
1506
|
+
}
|
|
1507
|
+
for (const relation of NAMED_READS) {
|
|
1508
|
+
for (const row of db.lookup(relation, 0, site)) {
|
|
1509
|
+
names.add(String(row[2]));
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return names.size === 1;
|
|
1514
|
+
}
|
|
1515
|
+
var NAMED_READS = ["choosesNamedEntry", "readsEntry"];
|
|
1516
|
+
function column(db, relation, key, at) {
|
|
1517
|
+
return [
|
|
1518
|
+
...new Set(db.lookup(relation, 0, key).map((row) => String(row[at])))
|
|
1519
|
+
];
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1257
1522
|
// src/entryReuse.ts
|
|
1258
1523
|
function fileOfKey(key) {
|
|
1259
1524
|
return key.slice(0, key.lastIndexOf(":"));
|
|
@@ -1783,14 +2048,21 @@ var SPAN_KEY = /^(.*):(\d+)-(\d+)$/;
|
|
|
1783
2048
|
function spanFileOf(key) {
|
|
1784
2049
|
return key.match(SPAN_KEY)?.[1] ?? null;
|
|
1785
2050
|
}
|
|
1786
|
-
function
|
|
2051
|
+
function spanOfKey(key) {
|
|
1787
2052
|
const span = key.match(SPAN_KEY);
|
|
2053
|
+
return span === null ? null : {
|
|
2054
|
+
file: span[1],
|
|
2055
|
+
start: Number(span[2]),
|
|
2056
|
+
end: Number(span[3])
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function nodeOfKey(rootsByFile, key) {
|
|
2060
|
+
const span = spanOfKey(key);
|
|
1788
2061
|
if (span === null) {
|
|
1789
2062
|
return null;
|
|
1790
2063
|
}
|
|
1791
|
-
const root = rootsByFile.get(span
|
|
1792
|
-
const start =
|
|
1793
|
-
const end = Number(span[3]);
|
|
2064
|
+
const root = rootsByFile.get(span.file);
|
|
2065
|
+
const { start, end } = span;
|
|
1794
2066
|
if (root === void 0 || end <= start) {
|
|
1795
2067
|
return null;
|
|
1796
2068
|
}
|
|
@@ -2361,6 +2633,165 @@ var STEP_LATTICE = [
|
|
|
2361
2633
|
{ soFar: RESULT_STEP, next: INSTANCE_STEP, took: RESULT_STEP },
|
|
2362
2634
|
{ soFar: RESULT_STEP, next: RESULT_STEP, took: RESULT_STEP }
|
|
2363
2635
|
];
|
|
2636
|
+
var DISPATCH_RULES = [
|
|
2637
|
+
rule2(
|
|
2638
|
+
"entryRead",
|
|
2639
|
+
[v2("site"), v2("o")],
|
|
2640
|
+
[lit2("choosesEntry", v2("site"), v2("o"))]
|
|
2641
|
+
),
|
|
2642
|
+
rule2(
|
|
2643
|
+
"entryRead",
|
|
2644
|
+
[v2("site"), v2("o")],
|
|
2645
|
+
[lit2("namedEntryRead", v2("site"), v2("o"), v2("n"))]
|
|
2646
|
+
),
|
|
2647
|
+
rule2(
|
|
2648
|
+
"entryRead",
|
|
2649
|
+
[v2("site"), v2("o")],
|
|
2650
|
+
[lit2("choosesEntryLike", v2("site"), v2("o"), v2("p"), v2("s"))]
|
|
2651
|
+
),
|
|
2652
|
+
// Every entry of a read whose key is partly written out, by name. A rule
|
|
2653
|
+
// cannot compare strings, so dispatch.ts keeps the names that match, and
|
|
2654
|
+
// these stay out of `dispatchesTo`.
|
|
2655
|
+
rule2(
|
|
2656
|
+
"likeEntryAt",
|
|
2657
|
+
[v2("site"), v2("n"), v2("held")],
|
|
2658
|
+
[
|
|
2659
|
+
lit2("choosesEntryLike", v2("site"), v2("o"), v2("p"), v2("s")),
|
|
2660
|
+
lit2("containerOf", v2("o"), v2("obj")),
|
|
2661
|
+
lit2("contains", v2("obj"), v2("n"), v2("held"))
|
|
2662
|
+
]
|
|
2663
|
+
),
|
|
2664
|
+
rule2(
|
|
2665
|
+
"namedEntryRead",
|
|
2666
|
+
[v2("site"), v2("o"), v2("n")],
|
|
2667
|
+
[lit2("choosesNamedEntry", v2("site"), v2("o"), v2("n"))]
|
|
2668
|
+
),
|
|
2669
|
+
rule2(
|
|
2670
|
+
"namedEntryRead",
|
|
2671
|
+
[v2("site"), v2("o"), v2("n")],
|
|
2672
|
+
[lit2("readsEntry", v2("site"), v2("o"), v2("n"))]
|
|
2673
|
+
),
|
|
2674
|
+
// The container a read takes its entry from: an object written out, or
|
|
2675
|
+
// a collection the program fills through its methods.
|
|
2676
|
+
rule2("containerOf", [v2("o"), v2("obj")], [lit2("objectOf", v2("o"), v2("obj"))]),
|
|
2677
|
+
rule2(
|
|
2678
|
+
"containerOf",
|
|
2679
|
+
[v2("o"), v2("c")],
|
|
2680
|
+
[
|
|
2681
|
+
lit2("reaches", v2("o"), v2("c"), VALUE_STEP),
|
|
2682
|
+
lit2("collectionValue", v2("c"))
|
|
2683
|
+
]
|
|
2684
|
+
),
|
|
2685
|
+
rule2("containerOf", [v2("c"), v2("c")], [lit2("collectionValue", v2("c"))]),
|
|
2686
|
+
rule2(
|
|
2687
|
+
"entryAt",
|
|
2688
|
+
[v2("site"), v2("held")],
|
|
2689
|
+
[
|
|
2690
|
+
lit2("choosesEntry", v2("site"), v2("o")),
|
|
2691
|
+
lit2("containerOf", v2("o"), v2("obj")),
|
|
2692
|
+
lit2("contains", v2("obj"), v2("n"), v2("held"))
|
|
2693
|
+
],
|
|
2694
|
+
"any entry"
|
|
2695
|
+
),
|
|
2696
|
+
rule2(
|
|
2697
|
+
"entryAt",
|
|
2698
|
+
[v2("site"), v2("held")],
|
|
2699
|
+
[
|
|
2700
|
+
lit2("namedEntryRead", v2("site"), v2("o"), v2("n")),
|
|
2701
|
+
lit2("containerOf", v2("o"), v2("obj")),
|
|
2702
|
+
lit2("contains", v2("obj"), v2("n"), v2("held"))
|
|
2703
|
+
],
|
|
2704
|
+
"named entry"
|
|
2705
|
+
),
|
|
2706
|
+
rule2(
|
|
2707
|
+
"entryAt",
|
|
2708
|
+
[v2("site"), v2("held")],
|
|
2709
|
+
[lit2("computedEntryAt", v2("site"), v2("held"))]
|
|
2710
|
+
),
|
|
2711
|
+
// A write under a key the source computes, `T[name] = fn` or a map's
|
|
2712
|
+
// `set`, could be under any name, so every read of the container may
|
|
2713
|
+
// come back with it, a read narrowed to one name included.
|
|
2714
|
+
rule2(
|
|
2715
|
+
"computedEntryAt",
|
|
2716
|
+
[v2("site"), v2("held")],
|
|
2717
|
+
[
|
|
2718
|
+
lit2("entryRead", v2("site"), v2("o")),
|
|
2719
|
+
lit2("containerOf", v2("o"), v2("c")),
|
|
2720
|
+
lit2("refersToObject", v2("x"), v2("c")),
|
|
2721
|
+
lit2("holdsUnderKey", v2("x"), v2("held"))
|
|
2722
|
+
],
|
|
2723
|
+
"entry under a computed key"
|
|
2724
|
+
),
|
|
2725
|
+
// The read a call takes its function from: the callee itself, a name
|
|
2726
|
+
// for it, or the call when the call is the read, as Ruby's `send` is.
|
|
2727
|
+
rule2(
|
|
2728
|
+
"callsEntryAt",
|
|
2729
|
+
[v2("r"), v2("site")],
|
|
2730
|
+
[lit2("call", v2("r"), v2("site")), lit2("entryRead", v2("site"), v2("o"))]
|
|
2731
|
+
),
|
|
2732
|
+
rule2(
|
|
2733
|
+
"callsEntryAt",
|
|
2734
|
+
[v2("r"), v2("site")],
|
|
2735
|
+
[
|
|
2736
|
+
lit2("call", v2("r"), v2("c")),
|
|
2737
|
+
lit2("reaches", v2("c"), v2("site"), VALUE_STEP),
|
|
2738
|
+
lit2("entryRead", v2("site"), v2("o"))
|
|
2739
|
+
]
|
|
2740
|
+
),
|
|
2741
|
+
rule2("callsEntryAt", [v2("r"), v2("r")], [lit2("callsChosenEntry", v2("r"))]),
|
|
2742
|
+
rule2(
|
|
2743
|
+
"callsEntryAt",
|
|
2744
|
+
[v2("r"), v2("site")],
|
|
2745
|
+
[lit2("callsEntryInBranch", v2("r"), v2("site"))]
|
|
2746
|
+
),
|
|
2747
|
+
// `c ? T[k] : undefined` is one of its branches, so a call through it
|
|
2748
|
+
// may run what the read gives. The other branch may be some other
|
|
2749
|
+
// function, so dispatch.ts never counts such a call as certain.
|
|
2750
|
+
rule2(
|
|
2751
|
+
"callsEntryInBranch",
|
|
2752
|
+
[v2("r"), v2("site")],
|
|
2753
|
+
[lit2("call", v2("r"), v2("c")), lit2("branchReadsEntry", v2("c"), v2("site"))]
|
|
2754
|
+
),
|
|
2755
|
+
rule2(
|
|
2756
|
+
"callsEntryInBranch",
|
|
2757
|
+
[v2("r"), v2("site")],
|
|
2758
|
+
[
|
|
2759
|
+
lit2("call", v2("r"), v2("c")),
|
|
2760
|
+
lit2("reaches", v2("c"), v2("x"), VALUE_STEP),
|
|
2761
|
+
lit2("branchReadsEntry", v2("x"), v2("site"))
|
|
2762
|
+
]
|
|
2763
|
+
),
|
|
2764
|
+
rule2(
|
|
2765
|
+
"branchReadsEntry",
|
|
2766
|
+
[v2("x"), v2("site")],
|
|
2767
|
+
[
|
|
2768
|
+
lit2("conditionalBranch", v2("x"), v2("site")),
|
|
2769
|
+
lit2("entryRead", v2("site"), v2("o"))
|
|
2770
|
+
]
|
|
2771
|
+
),
|
|
2772
|
+
rule2(
|
|
2773
|
+
"branchReadsEntry",
|
|
2774
|
+
[v2("x"), v2("site")],
|
|
2775
|
+
[
|
|
2776
|
+
lit2("conditionalBranch", v2("x"), v2("b")),
|
|
2777
|
+
lit2("reaches", v2("b"), v2("site"), VALUE_STEP),
|
|
2778
|
+
lit2("entryRead", v2("site"), v2("o"))
|
|
2779
|
+
]
|
|
2780
|
+
),
|
|
2781
|
+
rule2(
|
|
2782
|
+
"dispatchesTo",
|
|
2783
|
+
[v2("r"), v2("f")],
|
|
2784
|
+
[
|
|
2785
|
+
lit2("callsEntryAt", v2("r"), v2("site")),
|
|
2786
|
+
lit2("entryAt", v2("site"), v2("held")),
|
|
2787
|
+
lit2("comesTo", v2("held"), v2("f")),
|
|
2788
|
+
lit2("func", v2("f"))
|
|
2789
|
+
]
|
|
2790
|
+
),
|
|
2791
|
+
// A collection a program fills is an object the way a written one is,
|
|
2792
|
+
// so a write through a name for it lands on it.
|
|
2793
|
+
rule2("refersToObject", [v2("c"), v2("c")], [lit2("collectionValue", v2("c"))])
|
|
2794
|
+
];
|
|
2364
2795
|
var STATED_RULES = [
|
|
2365
2796
|
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
2366
2797
|
// Every chain that follows a name takes the name hops, so a hop added
|
|
@@ -3463,6 +3894,17 @@ var STATED_RULES = [
|
|
|
3463
3894
|
lit2("callsNamed", v2("r"), v2("r0"))
|
|
3464
3895
|
]
|
|
3465
3896
|
),
|
|
3897
|
+
// The same, called straight off the factory's call, as
|
|
3898
|
+
// `register("created")(handler)` or a Python decorator written that way.
|
|
3899
|
+
rule2(
|
|
3900
|
+
"callsFunction",
|
|
3901
|
+
[v2("r"), v2("f")],
|
|
3902
|
+
[
|
|
3903
|
+
lit2("returnsValue", v2("g"), v2("f")),
|
|
3904
|
+
lit2("callsFunction", v2("r0"), v2("g")),
|
|
3905
|
+
lit2("call", v2("r"), v2("r0"))
|
|
3906
|
+
]
|
|
3907
|
+
),
|
|
3466
3908
|
// The expressions that refer to an object under another name, through
|
|
3467
3909
|
// a name hop or an import. Asked from the object, so it visits only
|
|
3468
3910
|
// them.
|
|
@@ -3908,6 +4350,7 @@ var STATED_RULES = [
|
|
|
3908
4350
|
[v2("x"), v2("z")],
|
|
3909
4351
|
[lit2("comesTo", v2("x"), v2("z")), lit2("func", v2("z"))]
|
|
3910
4352
|
),
|
|
4353
|
+
...DISPATCH_RULES,
|
|
3911
4354
|
...PASSED_ON_RULES
|
|
3912
4355
|
];
|
|
3913
4356
|
function alsoSteps(rules) {
|
|
@@ -4111,18 +4554,112 @@ var RESOLUTION_QUESTIONS = [
|
|
|
4111
4554
|
),
|
|
4112
4555
|
// The functions whose return becomes the exit code: a call the value
|
|
4113
4556
|
// that ends up as the code is, or one it reaches. DESIGN.md has the seeds.
|
|
4557
|
+
rule2("exitStart", [v2("x"), v2("x")], [lit2("wantedExitSink", v2("x"))]),
|
|
4558
|
+
// `reaches` takes no step through a call that picks its function from a
|
|
4559
|
+
// table, so the walk starts again from what each function there returns.
|
|
4560
|
+
rule2(
|
|
4561
|
+
"exitStart",
|
|
4562
|
+
[v2("x"), v2("ret")],
|
|
4563
|
+
[
|
|
4564
|
+
lit2("exitReached", v2("x"), v2("y")),
|
|
4565
|
+
lit2("dispatchesTo", v2("y"), v2("f")),
|
|
4566
|
+
lit2("returnsValue", v2("f"), v2("ret"))
|
|
4567
|
+
]
|
|
4568
|
+
),
|
|
4569
|
+
rule2("exitReached", [v2("x"), v2("s")], [lit2("exitStart", v2("x"), v2("s"))]),
|
|
4570
|
+
rule2(
|
|
4571
|
+
"exitReached",
|
|
4572
|
+
[v2("x"), v2("y")],
|
|
4573
|
+
[
|
|
4574
|
+
lit2("exitStart", v2("x"), v2("s")),
|
|
4575
|
+
lit2("reaches", v2("s"), v2("y"), v2("kind"))
|
|
4576
|
+
]
|
|
4577
|
+
),
|
|
4114
4578
|
rule2(
|
|
4115
4579
|
"wantedExitCodeFrom",
|
|
4116
4580
|
[v2("x"), v2("f")],
|
|
4117
|
-
[lit2("
|
|
4581
|
+
[lit2("exitReached", v2("x"), v2("y")), lit2("invokes", v2("y"), v2("f"))]
|
|
4118
4582
|
),
|
|
4119
4583
|
rule2(
|
|
4120
4584
|
"wantedExitCodeFrom",
|
|
4121
4585
|
[v2("x"), v2("f")],
|
|
4586
|
+
[lit2("exitReached", v2("x"), v2("y")), lit2("dispatchesTo", v2("y"), v2("f"))]
|
|
4587
|
+
),
|
|
4588
|
+
// The functions a call may run when it takes one from a table, with the
|
|
4589
|
+
// entry each came from, and the entries that come to no function. The
|
|
4590
|
+
// reader in dispatch.ts decides whether the call runs exactly one.
|
|
4591
|
+
rule2(
|
|
4592
|
+
"wantedDispatchSite",
|
|
4593
|
+
[v2("r"), v2("site")],
|
|
4594
|
+
[lit2("wantedDispatch", v2("r")), lit2("callsEntryAt", v2("r"), v2("site"))]
|
|
4595
|
+
),
|
|
4596
|
+
rule2(
|
|
4597
|
+
"wantedDispatchEntry",
|
|
4598
|
+
[v2("r"), v2("held")],
|
|
4599
|
+
[
|
|
4600
|
+
lit2("wantedDispatch", v2("r")),
|
|
4601
|
+
lit2("callsEntryAt", v2("r"), v2("site")),
|
|
4602
|
+
lit2("entryAt", v2("site"), v2("held"))
|
|
4603
|
+
]
|
|
4604
|
+
),
|
|
4605
|
+
rule2(
|
|
4606
|
+
"wantedDispatchInBranch",
|
|
4607
|
+
[v2("r"), v2("site")],
|
|
4608
|
+
[
|
|
4609
|
+
lit2("wantedDispatch", v2("r")),
|
|
4610
|
+
lit2("callsEntryInBranch", v2("r"), v2("site"))
|
|
4611
|
+
]
|
|
4612
|
+
),
|
|
4613
|
+
rule2(
|
|
4614
|
+
"wantedDispatchComputed",
|
|
4615
|
+
[v2("r"), v2("held")],
|
|
4616
|
+
[
|
|
4617
|
+
lit2("wantedDispatch", v2("r")),
|
|
4618
|
+
lit2("callsEntryAt", v2("r"), v2("site")),
|
|
4619
|
+
lit2("computedEntryAt", v2("site"), v2("held"))
|
|
4620
|
+
]
|
|
4621
|
+
),
|
|
4622
|
+
rule2(
|
|
4623
|
+
"wantedDispatchFunction",
|
|
4624
|
+
[v2("r"), v2("held"), v2("f")],
|
|
4625
|
+
[
|
|
4626
|
+
lit2("wantedDispatch", v2("r")),
|
|
4627
|
+
lit2("callsEntryAt", v2("r"), v2("site")),
|
|
4628
|
+
lit2("entryAt", v2("site"), v2("held")),
|
|
4629
|
+
lit2("comesTo", v2("held"), v2("f")),
|
|
4630
|
+
lit2("func", v2("f"))
|
|
4631
|
+
]
|
|
4632
|
+
),
|
|
4633
|
+
rule2(
|
|
4634
|
+
"wantedDispatchLikeEntry",
|
|
4635
|
+
[v2("r"), v2("site"), v2("n"), v2("held")],
|
|
4636
|
+
[
|
|
4637
|
+
lit2("wantedDispatch", v2("r")),
|
|
4638
|
+
lit2("callsEntryAt", v2("r"), v2("site")),
|
|
4639
|
+
lit2("likeEntryAt", v2("site"), v2("n"), v2("held"))
|
|
4640
|
+
]
|
|
4641
|
+
),
|
|
4642
|
+
rule2(
|
|
4643
|
+
"wantedDispatchLikeFunction",
|
|
4644
|
+
[v2("r"), v2("held"), v2("f")],
|
|
4645
|
+
[
|
|
4646
|
+
lit2("wantedDispatch", v2("r")),
|
|
4647
|
+
lit2("callsEntryAt", v2("r"), v2("site")),
|
|
4648
|
+
lit2("likeEntryAt", v2("site"), v2("n"), v2("held")),
|
|
4649
|
+
lit2("comesTo", v2("held"), v2("f")),
|
|
4650
|
+
lit2("func", v2("f"))
|
|
4651
|
+
]
|
|
4652
|
+
),
|
|
4653
|
+
// What a function there calls in its own body, for an entry written as
|
|
4654
|
+
// a lambda, which no summary describes.
|
|
4655
|
+
rule2(
|
|
4656
|
+
"wantedDispatchThrough",
|
|
4657
|
+
[v2("r"), v2("f"), v2("g")],
|
|
4122
4658
|
[
|
|
4123
|
-
lit2("
|
|
4124
|
-
lit2("
|
|
4125
|
-
lit2("
|
|
4659
|
+
lit2("wantedDispatch", v2("r")),
|
|
4660
|
+
lit2("dispatchesTo", v2("r"), v2("f")),
|
|
4661
|
+
lit2("makesCall", v2("f"), v2("r2")),
|
|
4662
|
+
lit2("invokes", v2("r2"), v2("g"))
|
|
4126
4663
|
]
|
|
4127
4664
|
),
|
|
4128
4665
|
// The calls that hand a stream on, so the asker can read the callee's file.
|
|
@@ -4556,6 +5093,7 @@ export {
|
|
|
4556
5093
|
ASKING_RELATIONS,
|
|
4557
5094
|
BASE_CLASS_RULE,
|
|
4558
5095
|
DEFAULT_IMPORT_NAME,
|
|
5096
|
+
DISPATCH_QUESTION,
|
|
4559
5097
|
DependencyLedger,
|
|
4560
5098
|
EntryReuse,
|
|
4561
5099
|
FACT_CONTRACT_CASES,
|
|
@@ -4573,14 +5111,18 @@ export {
|
|
|
4573
5111
|
RESOLUTION_QUESTIONS,
|
|
4574
5112
|
RESOLUTION_RULES,
|
|
4575
5113
|
RESULT_STEP,
|
|
5114
|
+
SPREAD_ENTRY,
|
|
4576
5115
|
UNDER_ANSWER_RELATIONS,
|
|
4577
5116
|
UNDER_QUESTION_ROW_BUDGET,
|
|
4578
5117
|
UNDER_RUN_ROW_BUDGET,
|
|
4579
5118
|
UNPLACED_STORE_NAME,
|
|
4580
5119
|
USES_QUESTION,
|
|
5120
|
+
VALUE_ENTRY,
|
|
4581
5121
|
VALUE_STEP,
|
|
4582
5122
|
WatchedMap,
|
|
5123
|
+
addEntryRows,
|
|
4583
5124
|
addPackWords,
|
|
5125
|
+
addTemplateRows,
|
|
4584
5126
|
agreedMountPrefix,
|
|
4585
5127
|
allocationSitesOf,
|
|
4586
5128
|
alsoSteps,
|
|
@@ -4597,6 +5139,7 @@ export {
|
|
|
4597
5139
|
comesToUnder,
|
|
4598
5140
|
couldBeSettled,
|
|
4599
5141
|
declaredTypesOf,
|
|
5142
|
+
dispatchOutcomes,
|
|
4600
5143
|
exitCodeFunctions,
|
|
4601
5144
|
explainResolutionProof,
|
|
4602
5145
|
explainResolvedKey,
|
|
@@ -4618,6 +5161,7 @@ export {
|
|
|
4618
5161
|
packHashes,
|
|
4619
5162
|
proofRules,
|
|
4620
5163
|
queryFacts,
|
|
5164
|
+
readDispatchOutcomes,
|
|
4621
5165
|
readUses,
|
|
4622
5166
|
recordsToReplay,
|
|
4623
5167
|
renderExplanation,
|
|
@@ -4626,6 +5170,7 @@ export {
|
|
|
4626
5170
|
resolvedFunctions,
|
|
4627
5171
|
settledFunction,
|
|
4628
5172
|
sourceLeavesOf,
|
|
5173
|
+
spanOfKey,
|
|
4629
5174
|
startsAtName,
|
|
4630
5175
|
staysInItsFunction,
|
|
4631
5176
|
stillValid,
|