@suss/resolution 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/dist/index.cjs +507 -221
- package/dist/index.d.cts +71 -2
- package/dist/index.d.ts +71 -2
- package/dist/index.js +297 -13
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -26,7 +26,9 @@ __export(index_exports, {
|
|
|
26
26
|
RESOLUTION_RULES: () => RESOLUTION_RULES,
|
|
27
27
|
RESULT_STEP: () => RESULT_STEP,
|
|
28
28
|
VALUE_STEP: () => VALUE_STEP,
|
|
29
|
-
checkFactContract: () => checkFactContract
|
|
29
|
+
checkFactContract: () => checkFactContract,
|
|
30
|
+
explainResolutionProof: () => explainResolutionProof,
|
|
31
|
+
renderExplanation: () => renderExplanation
|
|
30
32
|
});
|
|
31
33
|
module.exports = __toCommonJS(index_exports);
|
|
32
34
|
|
|
@@ -145,6 +147,27 @@ var FACT_CONTRACT_CASES = [
|
|
|
145
147
|
return facts("callArg").length > 0 ? null : "a construction passes its argument nowhere, so the parameter it lands in has no value";
|
|
146
148
|
}
|
|
147
149
|
},
|
|
150
|
+
{
|
|
151
|
+
name: "a name declared as a fallback",
|
|
152
|
+
requires: "a module-level name declared as a fallback between a property read and a call, `cached || build()` say",
|
|
153
|
+
// The fallback expression states its branches; each branch has to be
|
|
154
|
+
// keyed as the value node itself, or the chain stops at the fallback
|
|
155
|
+
// instead of continuing into whichever branch resolves.
|
|
156
|
+
check: (facts) => {
|
|
157
|
+
const branches = facts("fallbackBranch");
|
|
158
|
+
if (branches.length < 2) {
|
|
159
|
+
return `expected a fallback with two branches, found ${branches.length}`;
|
|
160
|
+
}
|
|
161
|
+
if (distinct(branches.map((row) => row[0] ?? "")) !== 1) {
|
|
162
|
+
return "the two branches are keyed under different expressions, so nothing says they are alternatives for one value";
|
|
163
|
+
}
|
|
164
|
+
const call = facts("call")[0]?.[0];
|
|
165
|
+
if (call === void 0) {
|
|
166
|
+
return "the call branch is not written down as a call, so the branch that resolves has nothing behind it";
|
|
167
|
+
}
|
|
168
|
+
return branches.some((row) => row[1] === call) ? null : "no branch is the call node itself, so the chain cannot continue into the branch that resolves";
|
|
169
|
+
}
|
|
170
|
+
},
|
|
148
171
|
{
|
|
149
172
|
name: "a value another file declares",
|
|
150
173
|
requires: "two files, one declaring a value and the other reading it by name",
|
|
@@ -202,283 +225,544 @@ function checkFactContract(sources, emit, options = {}) {
|
|
|
202
225
|
}, Promise.resolve([]));
|
|
203
226
|
}
|
|
204
227
|
|
|
205
|
-
// src/
|
|
228
|
+
// src/explain.ts
|
|
206
229
|
var import_datalog = require("@suss/datalog");
|
|
207
|
-
|
|
208
|
-
|
|
230
|
+
function exportTrail(proof, describe) {
|
|
231
|
+
if (proof.kind !== "derived") {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
const name = (0, import_datalog.ruleLabel)(proof.rule);
|
|
235
|
+
if (name === "re-export") {
|
|
236
|
+
const [m, n, m2, n2] = proof.premises[0].tuple;
|
|
237
|
+
return [
|
|
238
|
+
`${describe(m)} takes ${String(n)} from ${describe(m2)}, where it is called ${String(n2)}`,
|
|
239
|
+
...exportTrail(proof.premises[1], describe)
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
if (name === "re-export all") {
|
|
243
|
+
const [m, m2] = proof.premises[0].tuple;
|
|
244
|
+
return [
|
|
245
|
+
`${describe(m)} forwards everything ${describe(m2)} exports`,
|
|
246
|
+
...exportTrail(proof.premises[1], describe)
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
function argumentReason(proof, describe) {
|
|
252
|
+
const [, p, a] = proof.tuple;
|
|
253
|
+
if (proof.kind !== "derived") {
|
|
254
|
+
return `${describe(p)} is a parameter, and a call passes it ${describe(a)}`;
|
|
255
|
+
}
|
|
256
|
+
const f = proof.premises[0].tuple[0];
|
|
257
|
+
return `${describe(p)} is a parameter of ${describe(f)}, and a call passes it ${describe(a)}`;
|
|
258
|
+
}
|
|
259
|
+
var DEFAULT_PHRASES = {
|
|
260
|
+
alias: ({ tuple, describe }) => ({
|
|
261
|
+
reason: `${describe(tuple[0])} is declared as ${describe(tuple[1])}`
|
|
262
|
+
}),
|
|
263
|
+
"last write": ({ tuple, describe }) => ({
|
|
264
|
+
reason: `${describe(tuple[0])} is written more than once, and the last write leaves it as ${describe(tuple[1])}`
|
|
265
|
+
}),
|
|
266
|
+
fallback: ({ tuple, describe }) => ({
|
|
267
|
+
reason: `${describe(tuple[0])} is a fallback expression, and ${describe(tuple[1])} is the branch that resolves`
|
|
268
|
+
}),
|
|
269
|
+
import: ({ tuple, premises, describe }) => {
|
|
270
|
+
const [, module2, name] = premises[0].tuple;
|
|
271
|
+
return {
|
|
272
|
+
reason: `${describe(tuple[0])} is imported from ${describe(module2)} under the name ${String(name)}`,
|
|
273
|
+
notes: exportTrail(premises[1], describe)
|
|
274
|
+
};
|
|
275
|
+
},
|
|
276
|
+
argument: ({ premises, describe }) => ({
|
|
277
|
+
reason: argumentReason(premises[0], describe)
|
|
278
|
+
}),
|
|
279
|
+
"property read": ({ tuple, premises, describe }) => {
|
|
280
|
+
const [, object, name] = premises[0].tuple;
|
|
281
|
+
const contains = premises[2];
|
|
282
|
+
const inherited = contains.kind === "derived" && contains.premises.length > 1;
|
|
283
|
+
return {
|
|
284
|
+
reason: `${describe(tuple[0])} reads ${String(name)} off ${describe(object)}, which contains ${describe(tuple[1])}`,
|
|
285
|
+
notes: inherited ? [`${String(name)} comes from a base class`] : []
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
"class instance": ({ tuple, premises, describe, inline }) => ({
|
|
289
|
+
reason: `${describe(tuple[0])} makes an instance of ${describe(tuple[1])}`,
|
|
290
|
+
notes: inline(premises[1])
|
|
291
|
+
}),
|
|
292
|
+
"factory unwrap": ({ tuple, premises, describe, inline }) => {
|
|
293
|
+
const factory = premises[1].tuple[1];
|
|
294
|
+
return {
|
|
295
|
+
reason: `${describe(tuple[0])} calls ${describe(factory)}, a factory that passes its argument through, so it comes down to ${describe(tuple[1])}`,
|
|
296
|
+
notes: inline(premises[1])
|
|
297
|
+
};
|
|
298
|
+
},
|
|
299
|
+
"declared wrapper": ({ tuple, premises, describe }) => {
|
|
300
|
+
const callee = String(premises[0].tuple[1]);
|
|
301
|
+
const module2 = String(premises[2].tuple[1]);
|
|
302
|
+
const argument = String(premises[1].tuple[1]);
|
|
303
|
+
return {
|
|
304
|
+
reason: `${describe(tuple[0])} calls ${callee} from ${module2}, which passes argument ${argument} through, so it comes down to ${describe(tuple[1])}`,
|
|
305
|
+
assumptions: [
|
|
306
|
+
`a pack declares that ${callee} from ${module2} passes argument ${argument} through to its result`
|
|
307
|
+
]
|
|
308
|
+
};
|
|
309
|
+
},
|
|
310
|
+
"call result": ({ tuple, premises, describe, inline }) => {
|
|
311
|
+
const invokes = premises[0];
|
|
312
|
+
const invoked = invokes.tuple[1];
|
|
313
|
+
const calleeWalks = invokes.kind === "derived" ? invokes.premises.filter(
|
|
314
|
+
(premise) => premise.kind === "derived" && (premise.relation === "comesTo" || premise.relation === "givesBack")
|
|
315
|
+
) : [];
|
|
316
|
+
return {
|
|
317
|
+
reason: `${describe(tuple[0])} runs ${describe(invoked)}, which returns ${describe(tuple[1])}`,
|
|
318
|
+
notes: calleeWalks.flatMap((premise) => inline(premise))
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var CHAIN_RELATIONS = /* @__PURE__ */ new Set([
|
|
323
|
+
"resolves",
|
|
324
|
+
"comesTo",
|
|
325
|
+
"givesBack",
|
|
326
|
+
"isWrittenAs",
|
|
327
|
+
"comesFrom",
|
|
328
|
+
"reaches",
|
|
329
|
+
"stepsTo"
|
|
330
|
+
]);
|
|
331
|
+
function inlineChain(proof, state) {
|
|
332
|
+
const explained = explainResolutionProof(proof, {
|
|
333
|
+
describe: state.describe,
|
|
334
|
+
phrases: state.phrases
|
|
335
|
+
});
|
|
336
|
+
if (explained === null || explained.steps.length === 0) {
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
if (explained.truncated) {
|
|
340
|
+
state.truncated = true;
|
|
341
|
+
}
|
|
342
|
+
return explained.steps.map((step) => step.reason);
|
|
343
|
+
}
|
|
344
|
+
function stepFrom(proof, state) {
|
|
345
|
+
const name = (0, import_datalog.ruleLabel)(proof.rule);
|
|
346
|
+
const phrase = state.phrases[name];
|
|
347
|
+
const from = proof.tuple[0];
|
|
348
|
+
const to = proof.tuple[1];
|
|
349
|
+
if (phrase === void 0) {
|
|
350
|
+
return {
|
|
351
|
+
from,
|
|
352
|
+
to,
|
|
353
|
+
rule: name,
|
|
354
|
+
reason: `${state.describe(from)} steps to ${state.describe(to)} (${name})`,
|
|
355
|
+
notes: [],
|
|
356
|
+
assumptions: []
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const said = phrase({
|
|
360
|
+
tuple: proof.tuple,
|
|
361
|
+
premises: proof.premises,
|
|
362
|
+
describe: state.describe,
|
|
363
|
+
inline: (nested) => inlineChain(nested, state)
|
|
364
|
+
});
|
|
365
|
+
return {
|
|
366
|
+
from,
|
|
367
|
+
to,
|
|
368
|
+
rule: name,
|
|
369
|
+
reason: said.reason,
|
|
370
|
+
notes: said.notes ?? [],
|
|
371
|
+
assumptions: said.assumptions ?? []
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function importStep(proof, state) {
|
|
375
|
+
const [x, module2, name] = proof.tuple;
|
|
376
|
+
return {
|
|
377
|
+
from: x,
|
|
378
|
+
to: module2,
|
|
379
|
+
rule: "import",
|
|
380
|
+
reason: `${state.describe(x)} is imported from ${state.describe(module2)} under the name ${String(name)}`,
|
|
381
|
+
notes: [],
|
|
382
|
+
assumptions: []
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
var flattenInto = (proof, state) => {
|
|
386
|
+
if (proof.kind === "truncated") {
|
|
387
|
+
state.truncated = true;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (proof.kind !== "derived") {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const relation = proof.relation;
|
|
394
|
+
if (relation === "stepsTo") {
|
|
395
|
+
state.steps.push(stepFrom(proof, state));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (relation === "reaches") {
|
|
399
|
+
for (const p of proof.premises) {
|
|
400
|
+
flattenInto(p, state);
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (relation === "comesFrom") {
|
|
405
|
+
if (proof.premises.length === 2) {
|
|
406
|
+
flattenInto(proof.premises[0], state);
|
|
407
|
+
state.steps.push(importStep(proof.premises[1], state));
|
|
408
|
+
} else {
|
|
409
|
+
state.steps.push(importStep(proof.premises[0], state));
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
for (const p of proof.premises) {
|
|
414
|
+
if (p.kind === "derived" && (p.relation === "reaches" || p.relation === "comesTo")) {
|
|
415
|
+
flattenInto(p, state);
|
|
416
|
+
}
|
|
417
|
+
if (p.kind === "truncated") {
|
|
418
|
+
state.truncated = true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
function explainResolutionProof(proof, options) {
|
|
423
|
+
if (proof.kind === "absence") {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
if (proof.kind === "derived" && !CHAIN_RELATIONS.has(proof.relation)) {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
const state = {
|
|
430
|
+
steps: [],
|
|
431
|
+
truncated: proof.kind === "truncated",
|
|
432
|
+
describe: options.describe,
|
|
433
|
+
phrases: { ...DEFAULT_PHRASES, ...options.phrases }
|
|
434
|
+
};
|
|
435
|
+
flattenInto(proof, state);
|
|
436
|
+
const atoms = proof.kind === "fact" || proof.kind === "truncated" ? [proof.tuple[0]] : [proof.tuple[0], ...state.steps.map((step) => step.to)];
|
|
437
|
+
return {
|
|
438
|
+
atoms,
|
|
439
|
+
steps: state.steps,
|
|
440
|
+
assumptions: state.steps.flatMap((step) => step.assumptions),
|
|
441
|
+
truncated: state.truncated
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function renderExplanation(explanation, describe) {
|
|
445
|
+
const lines = [];
|
|
446
|
+
if (explanation.steps.length > 0) {
|
|
447
|
+
lines.push(explanation.atoms.map((atom) => describe(atom)).join(" -> "));
|
|
448
|
+
}
|
|
449
|
+
for (const step of explanation.steps) {
|
|
450
|
+
lines.push(` ${step.reason}`);
|
|
451
|
+
for (const note of step.notes) {
|
|
452
|
+
lines.push(` ${note}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
for (const assumption of explanation.assumptions) {
|
|
456
|
+
lines.push(` assuming ${assumption}`);
|
|
457
|
+
}
|
|
458
|
+
if (explanation.truncated) {
|
|
459
|
+
lines.push(" the chain goes on past the proof depth cap");
|
|
460
|
+
}
|
|
461
|
+
return lines;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/index.ts
|
|
465
|
+
var import_datalog2 = require("@suss/datalog");
|
|
466
|
+
var VALUE_STEP = (0, import_datalog2.constant)("value");
|
|
467
|
+
var RESULT_STEP = (0, import_datalog2.constant)("result");
|
|
209
468
|
var RESOLUTION_RULES = [
|
|
210
469
|
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
211
470
|
// A language with a hop of its own, like JavaScript's `.bind`, states
|
|
212
471
|
// it as a step too, or every question but `comesTo` misses it.
|
|
213
|
-
(0,
|
|
472
|
+
(0, import_datalog2.rule)(
|
|
473
|
+
"stepsTo",
|
|
474
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP],
|
|
475
|
+
[(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))],
|
|
476
|
+
"alias"
|
|
477
|
+
),
|
|
214
478
|
// A name written more than once has the value the last write left
|
|
215
479
|
// there. The adapter works out which write that is and stays quiet
|
|
216
480
|
// when control flow decides, so such a name steps nowhere at all.
|
|
217
|
-
(0,
|
|
481
|
+
(0, import_datalog2.rule)(
|
|
482
|
+
"stepsTo",
|
|
483
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP],
|
|
484
|
+
[(0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))],
|
|
485
|
+
"last write"
|
|
486
|
+
),
|
|
487
|
+
// A fallback says the value is one of its branches, so each branch is
|
|
488
|
+
// a step. A branch that resolves to nothing makes no claim, and two
|
|
489
|
+
// branches resolving to different things fail the single-answer policy.
|
|
490
|
+
(0, import_datalog2.rule)(
|
|
218
491
|
"stepsTo",
|
|
219
|
-
[(0,
|
|
220
|
-
[(0,
|
|
492
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("b"), VALUE_STEP],
|
|
493
|
+
[(0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("b"))],
|
|
494
|
+
"fallback"
|
|
221
495
|
),
|
|
222
496
|
// An import steps to what the module exports under that name.
|
|
223
|
-
(0,
|
|
497
|
+
(0, import_datalog2.rule)(
|
|
224
498
|
"stepsTo",
|
|
225
|
-
[(0,
|
|
499
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("value"), VALUE_STEP],
|
|
226
500
|
[
|
|
227
|
-
(0,
|
|
228
|
-
(0,
|
|
229
|
-
]
|
|
501
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
|
|
502
|
+
(0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))
|
|
503
|
+
],
|
|
504
|
+
"import"
|
|
230
505
|
),
|
|
231
506
|
// A parameter steps to what a call passes it. A function called from
|
|
232
507
|
// several places leaves its parameter with more than one value, and a
|
|
233
508
|
// caller that needs those apart asks `paramAt`.
|
|
234
|
-
(0,
|
|
509
|
+
(0, import_datalog2.rule)(
|
|
235
510
|
"stepsTo",
|
|
236
|
-
[(0,
|
|
237
|
-
[(0,
|
|
511
|
+
[(0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a"), VALUE_STEP],
|
|
512
|
+
[(0, import_datalog2.lit)("passesArgument", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a"))],
|
|
513
|
+
"argument"
|
|
238
514
|
),
|
|
239
515
|
// Reading a property steps to what the object contains under that
|
|
240
516
|
// name, whichever way the object arrived: `routes.list` off a name,
|
|
241
517
|
// or `make(body).handle` off a call.
|
|
242
|
-
(0,
|
|
518
|
+
(0, import_datalog2.rule)(
|
|
243
519
|
"stepsTo",
|
|
244
|
-
[(0,
|
|
520
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("held"), VALUE_STEP],
|
|
245
521
|
[
|
|
246
|
-
(0,
|
|
247
|
-
(0,
|
|
248
|
-
(0,
|
|
249
|
-
]
|
|
522
|
+
(0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n")),
|
|
523
|
+
(0, import_datalog2.lit)("objectOf", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("obj")),
|
|
524
|
+
(0, import_datalog2.lit)("contains", (0, import_datalog2.variable)("obj"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held"))
|
|
525
|
+
],
|
|
526
|
+
"property read"
|
|
250
527
|
),
|
|
251
528
|
// Calling a class makes one of it, so the call steps to the class and
|
|
252
529
|
// a method read off the result is the one the class declares. The
|
|
253
530
|
// caveat below is about a factory function, and a class is not one.
|
|
254
|
-
(0,
|
|
531
|
+
(0, import_datalog2.rule)(
|
|
255
532
|
"stepsTo",
|
|
256
|
-
[(0,
|
|
533
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("cls"), VALUE_STEP],
|
|
257
534
|
[
|
|
258
|
-
(0,
|
|
259
|
-
(0,
|
|
260
|
-
(0,
|
|
261
|
-
]
|
|
535
|
+
(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
|
|
536
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("cls")),
|
|
537
|
+
(0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("cls"))
|
|
538
|
+
],
|
|
539
|
+
"class instance"
|
|
262
540
|
),
|
|
263
541
|
// Wrapper transparency, derived: calling a factory that returns a
|
|
264
542
|
// function which calls its parameter k steps to argument k.
|
|
265
|
-
(0,
|
|
543
|
+
(0, import_datalog2.rule)(
|
|
266
544
|
"stepsTo",
|
|
267
|
-
[(0,
|
|
545
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("a"), VALUE_STEP],
|
|
268
546
|
[
|
|
269
|
-
(0,
|
|
270
|
-
(0,
|
|
271
|
-
(0,
|
|
272
|
-
(0,
|
|
273
|
-
]
|
|
547
|
+
(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
|
|
548
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f")),
|
|
549
|
+
(0, import_datalog2.lit)("unwraps", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")),
|
|
550
|
+
(0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a"))
|
|
551
|
+
],
|
|
552
|
+
"factory unwrap"
|
|
274
553
|
),
|
|
275
554
|
// Wrapper transparency, declared: a pack says this callee wraps
|
|
276
555
|
// argument k. The callee has to come from the library the pack said,
|
|
277
556
|
// so a local object spelled the same way is not mistaken for it.
|
|
278
|
-
(0,
|
|
557
|
+
(0, import_datalog2.rule)(
|
|
279
558
|
"stepsTo",
|
|
280
|
-
[(0,
|
|
559
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("a"), VALUE_STEP],
|
|
281
560
|
[
|
|
282
|
-
(0,
|
|
283
|
-
(0,
|
|
284
|
-
(0,
|
|
285
|
-
(0,
|
|
286
|
-
(0,
|
|
287
|
-
]
|
|
561
|
+
(0, import_datalog2.lit)("calleeName", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("n")),
|
|
562
|
+
(0, import_datalog2.lit)("unwrapsByName", (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("k")),
|
|
563
|
+
(0, import_datalog2.lit)("wrapperModule", (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m")),
|
|
564
|
+
(0, import_datalog2.lit)("calleeOrigin", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("m")),
|
|
565
|
+
(0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a"))
|
|
566
|
+
],
|
|
567
|
+
"declared wrapper"
|
|
288
568
|
),
|
|
289
569
|
// The one step that runs a function forwards: a call steps to what
|
|
290
570
|
// the function it invokes returns.
|
|
291
|
-
(0,
|
|
571
|
+
(0, import_datalog2.rule)(
|
|
292
572
|
"stepsTo",
|
|
293
|
-
[(0,
|
|
294
|
-
[(0,
|
|
573
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("ret"), RESULT_STEP],
|
|
574
|
+
[(0, import_datalog2.lit)("invokes", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")), (0, import_datalog2.lit)("returnsValue", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("ret"))],
|
|
575
|
+
"call result"
|
|
295
576
|
),
|
|
296
577
|
// Where a walk gets to, and whether it ran a call on the way. A value
|
|
297
578
|
// step keeps whatever the rest of the walk was, and a result step
|
|
298
579
|
// makes the whole walk one that ran a call.
|
|
299
|
-
(0,
|
|
580
|
+
(0, import_datalog2.rule)(
|
|
300
581
|
"reaches",
|
|
301
|
-
[(0,
|
|
302
|
-
[(0,
|
|
582
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind")],
|
|
583
|
+
[(0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind"))]
|
|
303
584
|
),
|
|
304
|
-
(0,
|
|
585
|
+
(0, import_datalog2.rule)(
|
|
305
586
|
"reaches",
|
|
306
|
-
[(0,
|
|
587
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind")],
|
|
307
588
|
[
|
|
308
|
-
(0,
|
|
309
|
-
(0,
|
|
589
|
+
(0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP),
|
|
590
|
+
(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("kind"))
|
|
310
591
|
]
|
|
311
592
|
),
|
|
312
593
|
// Two rules rather than one leaving the rest of the walk's kind free.
|
|
313
594
|
// A free kind is a second question about the same relation, and a
|
|
314
595
|
// demand-driven run then derives both to answer either.
|
|
315
|
-
(0,
|
|
596
|
+
(0, import_datalog2.rule)(
|
|
316
597
|
"reaches",
|
|
317
|
-
[(0,
|
|
598
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP],
|
|
318
599
|
[
|
|
319
|
-
(0,
|
|
320
|
-
(0,
|
|
600
|
+
(0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
|
|
601
|
+
(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), VALUE_STEP)
|
|
321
602
|
]
|
|
322
603
|
),
|
|
323
|
-
(0,
|
|
604
|
+
(0, import_datalog2.rule)(
|
|
324
605
|
"reaches",
|
|
325
|
-
[(0,
|
|
606
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP],
|
|
326
607
|
[
|
|
327
|
-
(0,
|
|
328
|
-
(0,
|
|
608
|
+
(0, import_datalog2.lit)("stepsTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), RESULT_STEP),
|
|
609
|
+
(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"), RESULT_STEP)
|
|
329
610
|
]
|
|
330
611
|
),
|
|
331
612
|
// What a value comes down to: the walk that never ran a call, stopped
|
|
332
613
|
// at a function or an object. Something already written out comes to
|
|
333
614
|
// itself, and every other walk ends at one of those.
|
|
334
|
-
(0,
|
|
335
|
-
(0,
|
|
336
|
-
(0,
|
|
615
|
+
(0, import_datalog2.rule)("comesTo", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("func", (0, import_datalog2.variable)("x"))]),
|
|
616
|
+
(0, import_datalog2.rule)("comesTo", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("x"))]),
|
|
617
|
+
(0, import_datalog2.rule)(
|
|
337
618
|
"comesTo",
|
|
338
|
-
[(0,
|
|
339
|
-
[(0,
|
|
619
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
620
|
+
[(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("func", (0, import_datalog2.variable)("z"))]
|
|
340
621
|
),
|
|
341
|
-
(0,
|
|
622
|
+
(0, import_datalog2.rule)(
|
|
342
623
|
"comesTo",
|
|
343
|
-
[(0,
|
|
344
|
-
[(0,
|
|
624
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
625
|
+
[(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("z"))]
|
|
345
626
|
),
|
|
346
627
|
// The same stopping condition, for the walk that ran a call. A call
|
|
347
628
|
// gets no `comesTo` answer on purpose: coming back with what a
|
|
348
629
|
// factory returned would fight the unwrapping answer.
|
|
349
|
-
(0,
|
|
630
|
+
(0, import_datalog2.rule)(
|
|
350
631
|
"givesBack",
|
|
351
|
-
[(0,
|
|
352
|
-
[(0,
|
|
632
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
633
|
+
[(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP), (0, import_datalog2.lit)("func", (0, import_datalog2.variable)("z"))]
|
|
353
634
|
),
|
|
354
|
-
(0,
|
|
635
|
+
(0, import_datalog2.rule)(
|
|
355
636
|
"givesBack",
|
|
356
|
-
[(0,
|
|
357
|
-
[(0,
|
|
637
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
638
|
+
[(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), RESULT_STEP), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("z"))]
|
|
358
639
|
),
|
|
359
640
|
// Following a value to the expression it is written as, whatever kind
|
|
360
641
|
// that is. A GraphQL document is neither a function nor an object, so
|
|
361
642
|
// `comesTo` never reaches one.
|
|
362
|
-
(0,
|
|
363
|
-
(0,
|
|
364
|
-
(0,
|
|
643
|
+
(0, import_datalog2.rule)("isWrittenAs", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("writtenValue", (0, import_datalog2.variable)("x"))]),
|
|
644
|
+
(0, import_datalog2.rule)("isWrittenAs", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("x"))]),
|
|
645
|
+
(0, import_datalog2.rule)(
|
|
365
646
|
"isWrittenAs",
|
|
366
|
-
[(0,
|
|
367
|
-
[(0,
|
|
647
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
648
|
+
[(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("writtenValue", (0, import_datalog2.variable)("z"))]
|
|
368
649
|
),
|
|
369
|
-
(0,
|
|
650
|
+
(0, import_datalog2.rule)(
|
|
370
651
|
"isWrittenAs",
|
|
371
|
-
[(0,
|
|
372
|
-
[(0,
|
|
652
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
653
|
+
[(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), VALUE_STEP), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("z"))]
|
|
373
654
|
),
|
|
374
655
|
// What one call site put in a parameter, told apart from what the
|
|
375
656
|
// other callers passed.
|
|
376
|
-
(0,
|
|
657
|
+
(0, import_datalog2.rule)(
|
|
377
658
|
"paramAt",
|
|
378
|
-
[(0,
|
|
659
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("z")],
|
|
379
660
|
[
|
|
380
|
-
(0,
|
|
381
|
-
(0,
|
|
661
|
+
(0, import_datalog2.lit)("passesArgument", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a")),
|
|
662
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("a"), (0, import_datalog2.variable)("z"))
|
|
382
663
|
]
|
|
383
664
|
),
|
|
384
665
|
// An argument arriving at the parameter it is passed to, by position
|
|
385
666
|
// or by the name the caller wrote, keeping the call it went through
|
|
386
667
|
// so `paramAt` can tell two call sites apart.
|
|
387
|
-
(0,
|
|
668
|
+
(0, import_datalog2.rule)(
|
|
388
669
|
"passesArgument",
|
|
389
|
-
[(0,
|
|
670
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a")],
|
|
390
671
|
[
|
|
391
|
-
(0,
|
|
392
|
-
(0,
|
|
393
|
-
(0,
|
|
672
|
+
(0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("p")),
|
|
673
|
+
(0, import_datalog2.lit)("callsFunction", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")),
|
|
674
|
+
(0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a"))
|
|
394
675
|
]
|
|
395
676
|
),
|
|
396
|
-
(0,
|
|
677
|
+
(0, import_datalog2.rule)(
|
|
397
678
|
"passesArgument",
|
|
398
|
-
[(0,
|
|
679
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("a")],
|
|
399
680
|
[
|
|
400
|
-
(0,
|
|
401
|
-
(0,
|
|
402
|
-
(0,
|
|
681
|
+
(0, import_datalog2.lit)("paramNamed", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("p")),
|
|
682
|
+
(0, import_datalog2.lit)("callsFunction", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")),
|
|
683
|
+
(0, import_datalog2.lit)("callKeywordArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("a"))
|
|
403
684
|
]
|
|
404
685
|
),
|
|
405
686
|
// What a module exports: directly, or through re-export chains.
|
|
406
|
-
(0,
|
|
687
|
+
(0, import_datalog2.rule)(
|
|
407
688
|
"moduleExport",
|
|
408
|
-
[(0,
|
|
409
|
-
[(0,
|
|
689
|
+
[(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
|
|
690
|
+
[(0, import_datalog2.lit)("exportsAs", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))],
|
|
691
|
+
"export"
|
|
410
692
|
),
|
|
411
|
-
(0,
|
|
693
|
+
(0, import_datalog2.rule)(
|
|
412
694
|
"moduleExport",
|
|
413
|
-
[(0,
|
|
695
|
+
[(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
|
|
414
696
|
[
|
|
415
|
-
(0,
|
|
416
|
-
(0,
|
|
417
|
-
]
|
|
697
|
+
(0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")),
|
|
698
|
+
(0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"), (0, import_datalog2.variable)("value"))
|
|
699
|
+
],
|
|
700
|
+
"re-export"
|
|
418
701
|
),
|
|
419
|
-
(0,
|
|
702
|
+
(0, import_datalog2.rule)(
|
|
420
703
|
"moduleExport",
|
|
421
|
-
[(0,
|
|
704
|
+
[(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
|
|
422
705
|
[
|
|
423
|
-
(0,
|
|
424
|
-
(0,
|
|
425
|
-
]
|
|
706
|
+
(0, import_datalog2.lit)("reExportsAll", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("m2")),
|
|
707
|
+
(0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))
|
|
708
|
+
],
|
|
709
|
+
"re-export all"
|
|
426
710
|
),
|
|
427
711
|
// The function a call runs, written from the call's side because a
|
|
428
712
|
// caller asking what a call gives back has the call in hand.
|
|
429
|
-
(0,
|
|
713
|
+
(0, import_datalog2.rule)(
|
|
430
714
|
"invokes",
|
|
431
|
-
[(0,
|
|
432
|
-
[(0,
|
|
715
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
|
|
716
|
+
[(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")), (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))]
|
|
433
717
|
),
|
|
434
718
|
// The callee is itself a call: `daoBuilder()()`.
|
|
435
|
-
(0,
|
|
719
|
+
(0, import_datalog2.rule)(
|
|
436
720
|
"invokes",
|
|
437
|
-
[(0,
|
|
438
|
-
[(0,
|
|
721
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
|
|
722
|
+
[(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")), (0, import_datalog2.lit)("givesBack", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))]
|
|
439
723
|
),
|
|
440
724
|
// The object an expression refers to: a name through `comesTo`, a
|
|
441
725
|
// factory call through what it gives back. Naming the step is what
|
|
442
726
|
// makes `routes.list` and `make(body).handle` one rule.
|
|
443
|
-
(0,
|
|
727
|
+
(0, import_datalog2.rule)(
|
|
444
728
|
"objectOf",
|
|
445
|
-
[(0,
|
|
446
|
-
[(0,
|
|
729
|
+
[(0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("obj")],
|
|
730
|
+
[(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("obj")), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("obj"))]
|
|
447
731
|
),
|
|
448
|
-
(0,
|
|
732
|
+
(0, import_datalog2.rule)(
|
|
449
733
|
"objectOf",
|
|
450
|
-
[(0,
|
|
451
|
-
[(0,
|
|
734
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("obj")],
|
|
735
|
+
[(0, import_datalog2.lit)("givesBack", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("obj")), (0, import_datalog2.lit)("objectValue", (0, import_datalog2.variable)("obj"))]
|
|
452
736
|
),
|
|
453
737
|
// Which calls a function, found by the name the call is written as
|
|
454
738
|
// rather than by resolving every callee in the project. A caller knows
|
|
455
739
|
// the function and wants its call sites, so both of these start from
|
|
456
740
|
// the function. Starting from `call` instead asks what every call in
|
|
457
741
|
// the project imports, which was 72% of everything derived.
|
|
458
|
-
(0,
|
|
742
|
+
(0, import_datalog2.rule)(
|
|
459
743
|
"callsFunction",
|
|
460
|
-
[(0,
|
|
461
|
-
[(0,
|
|
744
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
|
|
745
|
+
[(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
|
|
462
746
|
),
|
|
463
|
-
(0,
|
|
747
|
+
(0, import_datalog2.rule)(
|
|
464
748
|
"callsFunction",
|
|
465
|
-
[(0,
|
|
749
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
|
|
466
750
|
[
|
|
467
|
-
(0,
|
|
468
|
-
(0,
|
|
469
|
-
(0,
|
|
751
|
+
(0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("f")),
|
|
752
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
|
|
753
|
+
(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))
|
|
470
754
|
]
|
|
471
755
|
),
|
|
472
756
|
// The same, for a language whose adapter writes the import down as a
|
|
473
757
|
// declaration and the call's callee as the name referring to it.
|
|
474
|
-
(0,
|
|
758
|
+
(0, import_datalog2.rule)(
|
|
475
759
|
"callsFunction",
|
|
476
|
-
[(0,
|
|
760
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("f")],
|
|
477
761
|
[
|
|
478
|
-
(0,
|
|
479
|
-
(0,
|
|
480
|
-
(0,
|
|
481
|
-
(0,
|
|
762
|
+
(0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("f")),
|
|
763
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("d"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
|
|
764
|
+
(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("d")),
|
|
765
|
+
(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))
|
|
482
766
|
]
|
|
483
767
|
),
|
|
484
768
|
// What an object contains, its base class included, so a method the base
|
|
@@ -486,47 +770,47 @@ var RESOLUTION_RULES = [
|
|
|
486
770
|
// declare gives two, and the caller decides. This is its own relation
|
|
487
771
|
// rather than more `holdsProperty`, which stays something an adapter
|
|
488
772
|
// states and the rules only read.
|
|
489
|
-
(0,
|
|
773
|
+
(0, import_datalog2.rule)(
|
|
490
774
|
"contains",
|
|
491
|
-
[(0,
|
|
492
|
-
[(0,
|
|
775
|
+
[(0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held")],
|
|
776
|
+
[(0, import_datalog2.lit)("holdsProperty", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held"))]
|
|
493
777
|
),
|
|
494
|
-
(0,
|
|
778
|
+
(0, import_datalog2.rule)(
|
|
495
779
|
"contains",
|
|
496
|
-
[(0,
|
|
780
|
+
[(0, import_datalog2.variable)("cls"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held")],
|
|
497
781
|
[
|
|
498
|
-
(0,
|
|
499
|
-
(0,
|
|
500
|
-
(0,
|
|
782
|
+
(0, import_datalog2.lit)("extends", (0, import_datalog2.variable)("cls"), (0, import_datalog2.variable)("base")),
|
|
783
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("base"), (0, import_datalog2.variable)("baseCls")),
|
|
784
|
+
(0, import_datalog2.lit)("contains", (0, import_datalog2.variable)("baseCls"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("held"))
|
|
501
785
|
]
|
|
502
786
|
),
|
|
503
787
|
// Which function a factory returns, and which of its parameters that
|
|
504
788
|
// function calls: together they are what makes it a wrapper.
|
|
505
|
-
(0,
|
|
789
|
+
(0, import_datalog2.rule)(
|
|
506
790
|
"returnsFunc",
|
|
507
|
-
[(0,
|
|
791
|
+
[(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("g")],
|
|
508
792
|
[
|
|
509
|
-
(0,
|
|
510
|
-
(0,
|
|
511
|
-
(0,
|
|
793
|
+
(0, import_datalog2.lit)("returnsValue", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("value")),
|
|
794
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("value"), (0, import_datalog2.variable)("g")),
|
|
795
|
+
(0, import_datalog2.lit)("func", (0, import_datalog2.variable)("g"))
|
|
512
796
|
]
|
|
513
797
|
),
|
|
514
798
|
// A call made by a nested closure counts as made by the function
|
|
515
799
|
// that declares it; the closure runs as part of that function.
|
|
516
|
-
(0,
|
|
517
|
-
(0,
|
|
800
|
+
(0, import_datalog2.rule)("bodyCallsDeep", [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")], [(0, import_datalog2.lit)("bodyCalls", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c"))]),
|
|
801
|
+
(0, import_datalog2.rule)(
|
|
518
802
|
"bodyCallsDeep",
|
|
519
|
-
[(0,
|
|
520
|
-
[(0,
|
|
803
|
+
[(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")],
|
|
804
|
+
[(0, import_datalog2.lit)("containsFn", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("g")), (0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("c"))]
|
|
521
805
|
),
|
|
522
|
-
(0,
|
|
806
|
+
(0, import_datalog2.rule)(
|
|
523
807
|
"unwraps",
|
|
524
|
-
[(0,
|
|
808
|
+
[(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")],
|
|
525
809
|
[
|
|
526
|
-
(0,
|
|
527
|
-
(0,
|
|
528
|
-
(0,
|
|
529
|
-
(0,
|
|
810
|
+
(0, import_datalog2.lit)("returnsFunc", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("g")),
|
|
811
|
+
(0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("c")),
|
|
812
|
+
(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("p")),
|
|
813
|
+
(0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("p"))
|
|
530
814
|
]
|
|
531
815
|
),
|
|
532
816
|
// Argument flow: which parameter a value traces back to. Directly
|
|
@@ -534,48 +818,48 @@ var RESOLUTION_RULES = [
|
|
|
534
818
|
// another unwrapping factory. This is what lets
|
|
535
819
|
// `createProtected(h) { return service.withAuth(h); }` unwrap:
|
|
536
820
|
// the returned call passes h through withAuth, which unwraps.
|
|
537
|
-
(0,
|
|
821
|
+
(0, import_datalog2.rule)(
|
|
538
822
|
"flowsToParam",
|
|
539
|
-
[(0,
|
|
823
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("p")],
|
|
540
824
|
[
|
|
541
|
-
(0,
|
|
542
|
-
(0,
|
|
825
|
+
(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("p")),
|
|
826
|
+
(0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("anyF"), (0, import_datalog2.variable)("anyK"), (0, import_datalog2.variable)("p"))
|
|
543
827
|
]
|
|
544
828
|
),
|
|
545
|
-
(0,
|
|
829
|
+
(0, import_datalog2.rule)(
|
|
546
830
|
"flowsToParam",
|
|
547
|
-
[(0,
|
|
831
|
+
[(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p")],
|
|
548
832
|
[
|
|
549
|
-
(0,
|
|
550
|
-
(0,
|
|
551
|
-
(0,
|
|
552
|
-
(0,
|
|
553
|
-
(0,
|
|
833
|
+
(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
|
|
834
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f")),
|
|
835
|
+
(0, import_datalog2.lit)("unwraps", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")),
|
|
836
|
+
(0, import_datalog2.lit)("callArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("a")),
|
|
837
|
+
(0, import_datalog2.lit)("flowsToParam", (0, import_datalog2.variable)("a"), (0, import_datalog2.variable)("p"))
|
|
554
838
|
]
|
|
555
839
|
),
|
|
556
|
-
(0,
|
|
840
|
+
(0, import_datalog2.rule)(
|
|
557
841
|
"unwraps",
|
|
558
|
-
[(0,
|
|
842
|
+
[(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k")],
|
|
559
843
|
[
|
|
560
|
-
(0,
|
|
561
|
-
(0,
|
|
562
|
-
(0,
|
|
844
|
+
(0, import_datalog2.lit)("returnsValue", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("value")),
|
|
845
|
+
(0, import_datalog2.lit)("flowsToParam", (0, import_datalog2.variable)("value"), (0, import_datalog2.variable)("p")),
|
|
846
|
+
(0, import_datalog2.lit)("paramOf", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("k"), (0, import_datalog2.variable)("p"))
|
|
563
847
|
]
|
|
564
848
|
),
|
|
565
849
|
// Where a name comes from, when what it refers to lives outside the
|
|
566
850
|
// source being read. A walk ends at something written out in source,
|
|
567
851
|
// so it never reaches a library's own function.
|
|
568
|
-
(0,
|
|
852
|
+
(0, import_datalog2.rule)(
|
|
569
853
|
"comesFrom",
|
|
570
|
-
[(0,
|
|
571
|
-
[(0,
|
|
854
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
855
|
+
[(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))]
|
|
572
856
|
),
|
|
573
|
-
(0,
|
|
857
|
+
(0, import_datalog2.rule)(
|
|
574
858
|
"comesFrom",
|
|
575
|
-
[(0,
|
|
859
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
576
860
|
[
|
|
577
|
-
(0,
|
|
578
|
-
(0,
|
|
861
|
+
(0, import_datalog2.lit)("reaches", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), VALUE_STEP),
|
|
862
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
579
863
|
]
|
|
580
864
|
),
|
|
581
865
|
// Calling f ends up calling the name n that module m exports. One hop
|
|
@@ -587,80 +871,80 @@ var RESOLUTION_RULES = [
|
|
|
587
871
|
// ambiguity: a project decorator that composes two library decorators
|
|
588
872
|
// applies both. A caller asks whether a particular (m, n) is among
|
|
589
873
|
// them.
|
|
590
|
-
(0,
|
|
874
|
+
(0, import_datalog2.rule)(
|
|
591
875
|
"callsInto",
|
|
592
|
-
[(0,
|
|
876
|
+
[(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
593
877
|
[
|
|
594
|
-
(0,
|
|
595
|
-
(0,
|
|
878
|
+
(0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")),
|
|
879
|
+
(0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
596
880
|
]
|
|
597
881
|
),
|
|
598
|
-
(0,
|
|
882
|
+
(0, import_datalog2.rule)(
|
|
599
883
|
"callsInto",
|
|
600
|
-
[(0,
|
|
884
|
+
[(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
601
885
|
[
|
|
602
|
-
(0,
|
|
603
|
-
(0,
|
|
604
|
-
(0,
|
|
886
|
+
(0, import_datalog2.lit)("bodyCallsDeep", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("c")),
|
|
887
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("g")),
|
|
888
|
+
(0, import_datalog2.lit)("callsInto", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
605
889
|
]
|
|
606
890
|
),
|
|
607
891
|
// The question callers ask: what a value comes to, narrowed to the
|
|
608
892
|
// functions. Objects appear in the middle of chains and never in an
|
|
609
893
|
// answer.
|
|
610
|
-
(0,
|
|
894
|
+
(0, import_datalog2.rule)(
|
|
611
895
|
"resolves",
|
|
612
|
-
[(0,
|
|
613
|
-
[(0,
|
|
896
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
897
|
+
[(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")), (0, import_datalog2.lit)("func", (0, import_datalog2.variable)("z"))]
|
|
614
898
|
)
|
|
615
899
|
];
|
|
616
900
|
var RESOLUTION_QUESTIONS = [
|
|
617
|
-
(0,
|
|
901
|
+
(0, import_datalog2.rule)(
|
|
618
902
|
"wantedResolves",
|
|
619
|
-
[(0,
|
|
620
|
-
[(0,
|
|
903
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
904
|
+
[(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("resolves", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
|
|
621
905
|
),
|
|
622
|
-
(0,
|
|
906
|
+
(0, import_datalog2.rule)(
|
|
623
907
|
"wantedComesTo",
|
|
624
|
-
[(0,
|
|
625
|
-
[(0,
|
|
908
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
909
|
+
[(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
|
|
626
910
|
),
|
|
627
|
-
(0,
|
|
911
|
+
(0, import_datalog2.rule)(
|
|
628
912
|
"wantedComesTo",
|
|
629
|
-
[(0,
|
|
630
|
-
[(0,
|
|
913
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
914
|
+
[(0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
|
|
631
915
|
),
|
|
632
|
-
(0,
|
|
916
|
+
(0, import_datalog2.rule)(
|
|
633
917
|
"wantedIsWrittenAs",
|
|
634
|
-
[(0,
|
|
635
|
-
[(0,
|
|
918
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
919
|
+
[(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
|
|
636
920
|
),
|
|
637
921
|
// A call is given no `comesTo`, so this is the only way to ask what
|
|
638
922
|
// object one arrives at, and without it a demand-driven run derives
|
|
639
923
|
// `objectOf` nowhere.
|
|
640
|
-
(0,
|
|
924
|
+
(0, import_datalog2.rule)(
|
|
641
925
|
"wantedObjectOf",
|
|
642
|
-
[(0,
|
|
643
|
-
[(0,
|
|
926
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
927
|
+
[(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("objectOf", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"))]
|
|
644
928
|
),
|
|
645
929
|
// Keyed by the parameter, since that is what a caller has in hand
|
|
646
930
|
// when it wants the call sites told apart.
|
|
647
|
-
(0,
|
|
931
|
+
(0, import_datalog2.rule)(
|
|
648
932
|
"wantedParamAt",
|
|
649
|
-
[(0,
|
|
650
|
-
[(0,
|
|
933
|
+
[(0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("z")],
|
|
934
|
+
[(0, import_datalog2.lit)("wanted", (0, import_datalog2.variable)("p")), (0, import_datalog2.lit)("paramAt", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("p"), (0, import_datalog2.variable)("z"))]
|
|
651
935
|
),
|
|
652
|
-
(0,
|
|
936
|
+
(0, import_datalog2.rule)(
|
|
653
937
|
"wantedComesFrom",
|
|
654
|
-
[(0,
|
|
655
|
-
[(0,
|
|
938
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
939
|
+
[(0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))]
|
|
656
940
|
),
|
|
657
|
-
(0,
|
|
941
|
+
(0, import_datalog2.rule)(
|
|
658
942
|
"wantedCallsInto",
|
|
659
|
-
[(0,
|
|
943
|
+
[(0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
660
944
|
[
|
|
661
|
-
(0,
|
|
662
|
-
(0,
|
|
663
|
-
(0,
|
|
945
|
+
(0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")),
|
|
946
|
+
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("g")),
|
|
947
|
+
(0, import_datalog2.lit)("callsInto", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
664
948
|
]
|
|
665
949
|
)
|
|
666
950
|
];
|
|
@@ -675,5 +959,7 @@ var ANSWER_RELATIONS = [
|
|
|
675
959
|
RESOLUTION_RULES,
|
|
676
960
|
RESULT_STEP,
|
|
677
961
|
VALUE_STEP,
|
|
678
|
-
checkFactContract
|
|
962
|
+
checkFactContract,
|
|
963
|
+
explainResolutionProof,
|
|
964
|
+
renderExplanation
|
|
679
965
|
});
|