@suss/adapter-ruby 0.35.0 → 0.36.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 +1 -1
- package/dist/index.cjs +1087 -474
- package/dist/index.d.cts +57 -12
- package/dist/index.d.ts +57 -12
- package/dist/index.js +1089 -456
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -627,6 +627,8 @@ import {
|
|
|
627
627
|
readUses,
|
|
628
628
|
resolutionProgram,
|
|
629
629
|
resolutionUnderProgram,
|
|
630
|
+
SELF_AS_CLASS,
|
|
631
|
+
SELF_AS_INSTANCE,
|
|
630
632
|
writtenValueOf as sharedWrittenValueOf,
|
|
631
633
|
writtenValueUnder as sharedWrittenValueUnder,
|
|
632
634
|
USES_QUESTION
|
|
@@ -720,6 +722,8 @@ var INVOKE_METHOD = "call";
|
|
|
720
722
|
var BLOCK_WRAPPERS = /* @__PURE__ */ new Set(["proc", "lambda"]);
|
|
721
723
|
var PROC_CLASS = "Proc";
|
|
722
724
|
var CONSTRUCTOR = "new";
|
|
725
|
+
var INITIALIZE_METHOD = "initialize";
|
|
726
|
+
var CLASS_OF_INSTANCE = "class";
|
|
723
727
|
|
|
724
728
|
// src/facts/resolve.ts
|
|
725
729
|
import { resolvedFunctions, settledFunction } from "@suss/resolution";
|
|
@@ -747,6 +751,26 @@ var RUBY_RULES = alsoSteps([
|
|
|
747
751
|
lit("readsProperty", v("c"), v("o"), constant(CONSTRUCTOR)),
|
|
748
752
|
lit("call", v("r"), v("c"))
|
|
749
753
|
]
|
|
754
|
+
),
|
|
755
|
+
// `new` in a class method makes one of the class the method runs on,
|
|
756
|
+
// and `self.class` in an instance method is that class.
|
|
757
|
+
rule(
|
|
758
|
+
"selfBehind",
|
|
759
|
+
[v("r"), v("s"), SELF_AS_INSTANCE],
|
|
760
|
+
[
|
|
761
|
+
lit("call", v("r"), v("c")),
|
|
762
|
+
lit("readsProperty", v("c"), v("o"), constant(CONSTRUCTOR)),
|
|
763
|
+
lit("selfBehind", v("o"), v("s"), SELF_AS_CLASS)
|
|
764
|
+
]
|
|
765
|
+
),
|
|
766
|
+
rule(
|
|
767
|
+
"selfBehind",
|
|
768
|
+
[v("r"), v("s"), SELF_AS_CLASS],
|
|
769
|
+
[
|
|
770
|
+
lit("call", v("r"), v("c")),
|
|
771
|
+
lit("readsProperty", v("c"), v("o"), constant(CLASS_OF_INSTANCE)),
|
|
772
|
+
lit("selfBehind", v("o"), v("s"), SELF_AS_INSTANCE)
|
|
773
|
+
]
|
|
750
774
|
)
|
|
751
775
|
]);
|
|
752
776
|
var RUBY_PROGRAM = resolutionProgram(RUBY_RULES);
|
|
@@ -796,6 +820,8 @@ import {
|
|
|
796
820
|
classMemberName,
|
|
797
821
|
NAMED_STORE_NAME,
|
|
798
822
|
RECEIVER_STORE_NAME,
|
|
823
|
+
SELF_AS_CLASS_NAME,
|
|
824
|
+
SELF_AS_INSTANCE_NAME,
|
|
799
825
|
SPREAD_ENTRY,
|
|
800
826
|
startsAtName,
|
|
801
827
|
UNPLACED_STORE_NAME,
|
|
@@ -2488,9 +2514,8 @@ function emitCandidates(emitter, key, group) {
|
|
|
2488
2514
|
add(emitter, "writesAllStated", key);
|
|
2489
2515
|
}
|
|
2490
2516
|
}
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
return definesClassMethod(method, emitter.bodyBlocks) ? classKey : `${nodeId(emitter.filePath, method)}#self`;
|
|
2517
|
+
function receiverKeyOf(file, method) {
|
|
2518
|
+
return `${nodeId(file, method)}#self`;
|
|
2494
2519
|
}
|
|
2495
2520
|
var CONSTANT_REF_TYPES = /* @__PURE__ */ new Set(["constant", "scope_resolution"]);
|
|
2496
2521
|
function mixedInConstants(body, callName, statements) {
|
|
@@ -2510,11 +2535,20 @@ function emitMixinFacts(emitter, classKey, body) {
|
|
|
2510
2535
|
}
|
|
2511
2536
|
function emitMethodOfClass(within, classKey, method, offeredOnClass) {
|
|
2512
2537
|
const stored = /* @__PURE__ */ new Map();
|
|
2513
|
-
const receiverKey = receiverKeyOf(within, method
|
|
2514
|
-
const onInstance =
|
|
2538
|
+
const receiverKey = receiverKeyOf(within.filePath, method);
|
|
2539
|
+
const onInstance = !definesClassMethod(method, within.bodyBlocks);
|
|
2515
2540
|
if (onInstance) {
|
|
2516
2541
|
add(within, "instanceOf", receiverKey, classKey);
|
|
2542
|
+
} else {
|
|
2543
|
+
add(within, "binds", receiverKey, classKey);
|
|
2517
2544
|
}
|
|
2545
|
+
add(
|
|
2546
|
+
within,
|
|
2547
|
+
"selfReceiver",
|
|
2548
|
+
nodeId(within.filePath, method),
|
|
2549
|
+
receiverKey,
|
|
2550
|
+
onInstance ? SELF_AS_INSTANCE_NAME : SELF_AS_CLASS_NAME
|
|
2551
|
+
);
|
|
2518
2552
|
const funcKey = emitMethodFacts(
|
|
2519
2553
|
{
|
|
2520
2554
|
...within,
|
|
@@ -4216,7 +4250,10 @@ async function definedInFileOf(fileConstant, qualifiedName, lookup) {
|
|
|
4216
4250
|
if (filePath === null) {
|
|
4217
4251
|
return null;
|
|
4218
4252
|
}
|
|
4219
|
-
|
|
4253
|
+
return blocksDefinedIn(filePath, qualifiedName, lookup.parsedFile);
|
|
4254
|
+
}
|
|
4255
|
+
async function blocksDefinedIn(filePath, qualifiedName, parsedFile) {
|
|
4256
|
+
const fileRoot = await parsedFile(filePath);
|
|
4220
4257
|
if (fileRoot === null) {
|
|
4221
4258
|
return null;
|
|
4222
4259
|
}
|
|
@@ -4296,9 +4333,16 @@ function unreadConstant(candidates) {
|
|
|
4296
4333
|
const bare = candidates.at(-1);
|
|
4297
4334
|
return bare === void 0 ? [] : [{ type: "unfollowed", name: bare }];
|
|
4298
4335
|
}
|
|
4299
|
-
function definitionOf(qualifiedName, lookup) {
|
|
4336
|
+
async function definitionOf(qualifiedName, lookup) {
|
|
4300
4337
|
const local = lookup.localDefinition?.(qualifiedName);
|
|
4301
|
-
|
|
4338
|
+
if (local !== void 0 && local !== null) {
|
|
4339
|
+
return local;
|
|
4340
|
+
}
|
|
4341
|
+
const inRun = await lookup.definedInRun?.(qualifiedName);
|
|
4342
|
+
if (inRun !== void 0 && inRun !== null) {
|
|
4343
|
+
return inRun;
|
|
4344
|
+
}
|
|
4345
|
+
return reachDefinition(qualifiedName, lookup);
|
|
4302
4346
|
}
|
|
4303
4347
|
async function mixinChain(self, callName, lookup, active, present) {
|
|
4304
4348
|
let chain = [];
|
|
@@ -4443,7 +4487,69 @@ import {
|
|
|
4443
4487
|
graphqlResolverBinding,
|
|
4444
4488
|
restBinding as restBinding2
|
|
4445
4489
|
} from "@suss/behavioral-ir";
|
|
4446
|
-
import { absentReading as absentReading3, unreadableReading as
|
|
4490
|
+
import { absentReading as absentReading3, unreadableReading as unreadableReading3 } from "@suss/extractor";
|
|
4491
|
+
|
|
4492
|
+
// src/baseClass.ts
|
|
4493
|
+
import { answersFor, askResolution as askResolution2, spanOfKey as spanOfKey2 } from "@suss/resolution";
|
|
4494
|
+
function classBehind(facts, file, constant2) {
|
|
4495
|
+
const key = constant2.type === "constant" ? `${file}#${constant2.text}` : nodeId(file, constant2);
|
|
4496
|
+
const bound = new Set(
|
|
4497
|
+
facts.lookup("binds", 0, key).map((row) => String(row[1]))
|
|
4498
|
+
);
|
|
4499
|
+
return bound.size === 1 ? [...bound][0] : void 0;
|
|
4500
|
+
}
|
|
4501
|
+
function classSettledOn(facts, key) {
|
|
4502
|
+
askResolution2(facts, [key], "wanted", RUBY_PROGRAM);
|
|
4503
|
+
const objects = answersFor(facts, "wantedObjectOf", key);
|
|
4504
|
+
return objects.length === 1 ? objects[0] : void 0;
|
|
4505
|
+
}
|
|
4506
|
+
function declaredName(facts, classKey) {
|
|
4507
|
+
const names = new Set(
|
|
4508
|
+
facts.lookup("rbConstantName", 0, classKey).map((row) => String(row[1]))
|
|
4509
|
+
);
|
|
4510
|
+
return names.size === 1 ? [...names][0] : void 0;
|
|
4511
|
+
}
|
|
4512
|
+
function classesDeclaredInRun(facts, parsedFile) {
|
|
4513
|
+
const known = /* @__PURE__ */ new Map();
|
|
4514
|
+
const read = async (qualifiedName) => {
|
|
4515
|
+
const files = /* @__PURE__ */ new Set();
|
|
4516
|
+
for (const row of facts.lookup("rbConstantName", 1, qualifiedName)) {
|
|
4517
|
+
const span = spanOfKey2(String(row[0]));
|
|
4518
|
+
if (span !== null) {
|
|
4519
|
+
files.add(span.file);
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
const blocks = [];
|
|
4523
|
+
for (const file of files) {
|
|
4524
|
+
blocks.push(
|
|
4525
|
+
...await blocksDefinedIn(file, qualifiedName, parsedFile) ?? []
|
|
4526
|
+
);
|
|
4527
|
+
}
|
|
4528
|
+
return blocks.length === 0 ? null : blocks;
|
|
4529
|
+
};
|
|
4530
|
+
return (qualifiedName) => {
|
|
4531
|
+
let found = known.get(qualifiedName);
|
|
4532
|
+
if (found === void 0) {
|
|
4533
|
+
found = read(qualifiedName);
|
|
4534
|
+
known.set(qualifiedName, found);
|
|
4535
|
+
}
|
|
4536
|
+
return found;
|
|
4537
|
+
};
|
|
4538
|
+
}
|
|
4539
|
+
function reachesBase(facts, classKey, bases) {
|
|
4540
|
+
askResolution2(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
|
|
4541
|
+
return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
|
|
4542
|
+
}
|
|
4543
|
+
function enclosingClassKey(node, file) {
|
|
4544
|
+
let current = node.parent;
|
|
4545
|
+
while (current !== null) {
|
|
4546
|
+
if (NESTING_TYPES.has(current.type)) {
|
|
4547
|
+
return nodeId(file, current);
|
|
4548
|
+
}
|
|
4549
|
+
current = current.parent;
|
|
4550
|
+
}
|
|
4551
|
+
return null;
|
|
4552
|
+
}
|
|
4447
4553
|
|
|
4448
4554
|
// src/clientCalls.ts
|
|
4449
4555
|
import {
|
|
@@ -4456,7 +4562,7 @@ import {
|
|
|
4456
4562
|
import { hostOf, pathOf } from "@suss/values";
|
|
4457
4563
|
|
|
4458
4564
|
// src/paths/effects.ts
|
|
4459
|
-
import {
|
|
4565
|
+
import { sharedGatingConditions, statementSites } from "@suss/extractor";
|
|
4460
4566
|
import { constantOf, literalOf as literalOf4 } from "@suss/values";
|
|
4461
4567
|
|
|
4462
4568
|
// src/paths/lowering.ts
|
|
@@ -4668,6 +4774,7 @@ var Lowerer = class {
|
|
|
4668
4774
|
);
|
|
4669
4775
|
return {
|
|
4670
4776
|
kind: "try",
|
|
4777
|
+
lines: rangeOf(node),
|
|
4671
4778
|
tryBody: tryBody.map((statement) => this.lower(statement)),
|
|
4672
4779
|
catchBody: rescues2.length > 0 ? rescues2.flatMap((clause) => this.lowerBlock(field(clause, "body"))) : null,
|
|
4673
4780
|
finallyBody: ensureClause === null ? null : this.lowerBlock(ensureClause),
|
|
@@ -4759,7 +4866,8 @@ var Lowerer = class {
|
|
|
4759
4866
|
kind: "loop",
|
|
4760
4867
|
condition: handleOf(header),
|
|
4761
4868
|
body: this.lowerBlock(field(node, "body")),
|
|
4762
|
-
exitKind: exitKindOf(node)
|
|
4869
|
+
exitKind: exitKindOf(node),
|
|
4870
|
+
lines: rangeOf(node)
|
|
4763
4871
|
},
|
|
4764
4872
|
[header]
|
|
4765
4873
|
);
|
|
@@ -4775,7 +4883,8 @@ var Lowerer = class {
|
|
|
4775
4883
|
kind: "loop",
|
|
4776
4884
|
condition: callHeaderOf(node, attachedBlock),
|
|
4777
4885
|
body: this.lowerBlock(field(attachedBlock, "body") ?? attachedBlock),
|
|
4778
|
-
exitKind: exitKindOf(node)
|
|
4886
|
+
exitKind: exitKindOf(node),
|
|
4887
|
+
lines: rangeOf(node)
|
|
4779
4888
|
},
|
|
4780
4889
|
[node]
|
|
4781
4890
|
);
|
|
@@ -4819,6 +4928,9 @@ var BLOCK_TYPES5 = /* @__PURE__ */ new Set([
|
|
|
4819
4928
|
"block"
|
|
4820
4929
|
]);
|
|
4821
4930
|
function literalArgOf(node, facts) {
|
|
4931
|
+
if (node.type === "nil") {
|
|
4932
|
+
return { kind: "null" };
|
|
4933
|
+
}
|
|
4822
4934
|
const value = evaluatedValue(node, facts);
|
|
4823
4935
|
const text3 = literalOf4(value);
|
|
4824
4936
|
if (text3 !== null) {
|
|
@@ -4956,13 +5068,21 @@ function argsOf(call, facts) {
|
|
|
4956
5068
|
function enclosingStatement(call, body) {
|
|
4957
5069
|
let current = call;
|
|
4958
5070
|
while (current.parent !== null) {
|
|
4959
|
-
if (
|
|
5071
|
+
if (listsStatements(current.parent, body)) {
|
|
4960
5072
|
return current;
|
|
4961
5073
|
}
|
|
4962
5074
|
current = current.parent;
|
|
4963
5075
|
}
|
|
4964
5076
|
return current;
|
|
4965
5077
|
}
|
|
5078
|
+
function listsStatements(node, body) {
|
|
5079
|
+
if (BLOCK_TYPES5.has(node.type) || node.id === body.id) {
|
|
5080
|
+
return true;
|
|
5081
|
+
}
|
|
5082
|
+
const begin = node.type === "ensure" ? node.parent : node;
|
|
5083
|
+
const around = begin?.type === "begin" ? begin.parent : null;
|
|
5084
|
+
return around !== null && around !== void 0 && (BLOCK_TYPES5.has(around.type) || around.id === body.id);
|
|
5085
|
+
}
|
|
4966
5086
|
function invocationEffects(definitionNode, inherited = NO_INHERITED_METHODS, keepsArglessCall = NO_ARGLESS_CALLS, facts) {
|
|
4967
5087
|
return invocationSites(
|
|
4968
5088
|
definitionNode,
|
|
@@ -4987,36 +5107,13 @@ function moduleScopeInvocationEffects(root, inherited = NO_INHERITED_METHODS, ke
|
|
|
4987
5107
|
).map((site) => site.effect);
|
|
4988
5108
|
}
|
|
4989
5109
|
function effectsOfBody(read, inherited, keepsArglessCall, facts) {
|
|
4990
|
-
const body = read.body;
|
|
4991
5110
|
const calls = callsReported(bodyCalls(read, inherited), keepsArglessCall);
|
|
4992
5111
|
if (calls.length === 0) {
|
|
4993
5112
|
return [];
|
|
4994
5113
|
}
|
|
4995
|
-
const
|
|
4996
|
-
const byStatement = new NodeMap();
|
|
4997
|
-
const statements = [];
|
|
4998
|
-
for (const call of calls) {
|
|
4999
|
-
const written = enclosingStatement(call, body);
|
|
5000
|
-
const statement = byStatement.get(written);
|
|
5001
|
-
if (statement === void 0) {
|
|
5002
|
-
byStatement.set(written, written);
|
|
5003
|
-
statements.push(written);
|
|
5004
|
-
}
|
|
5005
|
-
statementOf2.set(call.id, statement ?? written);
|
|
5006
|
-
}
|
|
5007
|
-
const lowered = lowerRubyBody(body, statements);
|
|
5008
|
-
const enumerated = enumerateOrDegrade(
|
|
5009
|
-
{
|
|
5010
|
-
statements: lowered.statements,
|
|
5011
|
-
terminalsByStmt: lowered.terminalsByStmt
|
|
5012
|
-
},
|
|
5013
|
-
statements
|
|
5014
|
-
);
|
|
5114
|
+
const sites = sitesIn(read.body, calls);
|
|
5015
5115
|
return calls.map((call) => {
|
|
5016
|
-
const
|
|
5017
|
-
const conditions = sharedGatingConditions(
|
|
5018
|
-
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement)
|
|
5019
|
-
);
|
|
5116
|
+
const conditions = sharedGatingConditions(sites.pathsTo(call));
|
|
5020
5117
|
return {
|
|
5021
5118
|
call,
|
|
5022
5119
|
effect: {
|
|
@@ -5024,11 +5121,26 @@ function effectsOfBody(read, inherited, keepsArglessCall, facts) {
|
|
|
5024
5121
|
callee: calleeText(call),
|
|
5025
5122
|
args: argsOf(call, facts),
|
|
5026
5123
|
async: false,
|
|
5027
|
-
...conditions.length > 0 ? { preconditions: conditions } : {}
|
|
5028
|
-
|
|
5124
|
+
...conditions.length > 0 ? { preconditions: conditions } : {},
|
|
5125
|
+
location: rangeOf(call)
|
|
5126
|
+
},
|
|
5127
|
+
site: sites.siteOf(call, conditions)
|
|
5029
5128
|
};
|
|
5030
5129
|
});
|
|
5031
5130
|
}
|
|
5131
|
+
function sitesIn(body, nodes) {
|
|
5132
|
+
return statementSites(nodes, {
|
|
5133
|
+
statementOf: (node) => enclosingStatement(node, body),
|
|
5134
|
+
lower: (statements) => lowerRubyBody(body, statements),
|
|
5135
|
+
firstLine: (node) => rangeOf(node).start
|
|
5136
|
+
});
|
|
5137
|
+
}
|
|
5138
|
+
function effectSitesIn(read, nodes) {
|
|
5139
|
+
if (read === null || nodes.length === 0) {
|
|
5140
|
+
return (node) => ({ line: rangeOf(node).start });
|
|
5141
|
+
}
|
|
5142
|
+
return sitesIn(read.body, nodes).plainSiteOf;
|
|
5143
|
+
}
|
|
5032
5144
|
|
|
5033
5145
|
// src/paths/predicates.ts
|
|
5034
5146
|
var OPERATORS = {
|
|
@@ -5415,15 +5527,10 @@ function raiseTerminal(call) {
|
|
|
5415
5527
|
}
|
|
5416
5528
|
function throughResponseHelpers(branches, sites, options) {
|
|
5417
5529
|
const handOffs = [];
|
|
5418
|
-
for (const { call,
|
|
5530
|
+
for (const { call, site } of sites) {
|
|
5419
5531
|
const helper = helperBranchesAt(call, options);
|
|
5420
5532
|
if (helper !== null) {
|
|
5421
|
-
handOffs.push({
|
|
5422
|
-
helper,
|
|
5423
|
-
preconditions: effect.preconditions,
|
|
5424
|
-
line: rangeOf(call).start,
|
|
5425
|
-
at: rangeOf(call)
|
|
5426
|
-
});
|
|
5533
|
+
handOffs.push({ ...site, helper, at: rangeOf(call) });
|
|
5427
5534
|
}
|
|
5428
5535
|
}
|
|
5429
5536
|
return handOffs.length === 0 ? [...branches] : branches.flatMap((branch) => splitAtHandOffs(branch, handOffs));
|
|
@@ -5486,8 +5593,8 @@ function sameNode(a, b) {
|
|
|
5486
5593
|
// src/responseStatus.ts
|
|
5487
5594
|
import {
|
|
5488
5595
|
absentReading as absentReading2,
|
|
5489
|
-
|
|
5490
|
-
|
|
5596
|
+
effectsFiringOn,
|
|
5597
|
+
enumerateOrDegrade,
|
|
5491
5598
|
unreadableReading,
|
|
5492
5599
|
writtenReading as writtenReading2
|
|
5493
5600
|
} from "@suss/extractor";
|
|
@@ -5499,31 +5606,7 @@ import { constantOf as constantOf2, literalOf as literalOf6 } from "@suss/values
|
|
|
5499
5606
|
|
|
5500
5607
|
// src/storage.ts
|
|
5501
5608
|
import { storageBinding as storageBinding2 } from "@suss/ir-core";
|
|
5502
|
-
import {
|
|
5503
|
-
|
|
5504
|
-
// src/baseClass.ts
|
|
5505
|
-
import { askResolution as askResolution2 } from "@suss/resolution";
|
|
5506
|
-
function classBehind(facts, file, constant2) {
|
|
5507
|
-
const key = constant2.type === "constant" ? `${file}#${constant2.text}` : nodeId(file, constant2);
|
|
5508
|
-
const bound = new Set(
|
|
5509
|
-
facts.lookup("binds", 0, key).map((row) => String(row[1]))
|
|
5510
|
-
);
|
|
5511
|
-
return bound.size === 1 ? [...bound][0] : void 0;
|
|
5512
|
-
}
|
|
5513
|
-
function reachesBase(facts, classKey, bases) {
|
|
5514
|
-
askResolution2(facts, [classKey], "wantedAncestry", RUBY_PROGRAM);
|
|
5515
|
-
return facts.lookup("wantedBaseName", 0, classKey).some((row) => bases.includes(String(row[1])));
|
|
5516
|
-
}
|
|
5517
|
-
function enclosingClassKey(node, file) {
|
|
5518
|
-
let current = node.parent;
|
|
5519
|
-
while (current !== null) {
|
|
5520
|
-
if (NESTING_TYPES.has(current.type)) {
|
|
5521
|
-
return nodeId(file, current);
|
|
5522
|
-
}
|
|
5523
|
-
current = current.parent;
|
|
5524
|
-
}
|
|
5525
|
-
return null;
|
|
5526
|
-
}
|
|
5609
|
+
import { askResolution as askResolution3, classMemberName as classMemberName2 } from "@suss/resolution";
|
|
5527
5610
|
|
|
5528
5611
|
// src/loaders.ts
|
|
5529
5612
|
function receiverOf(node) {
|
|
@@ -5668,7 +5751,8 @@ function effectOf(call, store, address, access) {
|
|
|
5668
5751
|
fields: access.fields,
|
|
5669
5752
|
operation,
|
|
5670
5753
|
...access.selector.length > 0 ? { selector: access.selector } : {}
|
|
5671
|
-
}
|
|
5754
|
+
},
|
|
5755
|
+
location: rangeOf(call)
|
|
5672
5756
|
};
|
|
5673
5757
|
}
|
|
5674
5758
|
function addressBehind(receiver, pattern, options, hops) {
|
|
@@ -5979,20 +6063,10 @@ function storageEffect(call, container, pattern, kind, selector, fields) {
|
|
|
5979
6063
|
fields,
|
|
5980
6064
|
operation,
|
|
5981
6065
|
...selector.length > 0 ? { selector } : {}
|
|
5982
|
-
}
|
|
6066
|
+
},
|
|
6067
|
+
location: rangeOf(call)
|
|
5983
6068
|
};
|
|
5984
6069
|
}
|
|
5985
|
-
function classSettledOn(facts, key) {
|
|
5986
|
-
askResolution3(facts, [key], "wanted", RUBY_PROGRAM);
|
|
5987
|
-
const objects = answersFor(facts, "wantedObjectOf", key);
|
|
5988
|
-
return objects.length === 1 ? objects[0] : void 0;
|
|
5989
|
-
}
|
|
5990
|
-
function declaredName(facts, classKey) {
|
|
5991
|
-
const names = new Set(
|
|
5992
|
-
facts.lookup("rbConstantName", 0, classKey).map((row) => String(row[1]))
|
|
5993
|
-
);
|
|
5994
|
-
return names.size === 1 ? [...names][0] : void 0;
|
|
5995
|
-
}
|
|
5996
6070
|
function projectDeclares(facts, classKey, method) {
|
|
5997
6071
|
const spellings = /* @__PURE__ */ new Set([method, classMemberName2(method)]);
|
|
5998
6072
|
return facts.lookup("wantedDeclaredName", 0, classKey).some((row) => spellings.has(String(row[1])));
|
|
@@ -6049,12 +6123,13 @@ function callbacksRunBy(facts, classKey, pattern, writeMethod) {
|
|
|
6049
6123
|
}
|
|
6050
6124
|
return [...found.values()];
|
|
6051
6125
|
}
|
|
6052
|
-
function callbackEffect(callback) {
|
|
6126
|
+
function callbackEffect(callback, write) {
|
|
6053
6127
|
return {
|
|
6054
6128
|
type: "invocation",
|
|
6055
6129
|
callee: callback.name,
|
|
6056
6130
|
args: [],
|
|
6057
|
-
async: false
|
|
6131
|
+
async: false,
|
|
6132
|
+
location: rangeOf(write)
|
|
6058
6133
|
};
|
|
6059
6134
|
}
|
|
6060
6135
|
function modelCallEffects(call, file, options, enclosing) {
|
|
@@ -6093,7 +6168,7 @@ function modelCallEffects(call, file, options, enclosing) {
|
|
|
6093
6168
|
options.statesSlots(access, slots);
|
|
6094
6169
|
}
|
|
6095
6170
|
}
|
|
6096
|
-
return [access, ...ran.map(callbackEffect)];
|
|
6171
|
+
return [access, ...ran.map((callback) => callbackEffect(callback, worked))];
|
|
6097
6172
|
}
|
|
6098
6173
|
return [];
|
|
6099
6174
|
}
|
|
@@ -6231,11 +6306,12 @@ function storageEffects(calls, file, options, enclosing = null) {
|
|
|
6231
6306
|
"wanted",
|
|
6232
6307
|
RUBY_PROGRAM
|
|
6233
6308
|
);
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6309
|
+
return chains.flatMap(
|
|
6310
|
+
(call) => effectsOfCall(call, file, options, enclosing).map((effect) => ({
|
|
6311
|
+
effect,
|
|
6312
|
+
at: call
|
|
6313
|
+
}))
|
|
6314
|
+
);
|
|
6239
6315
|
}
|
|
6240
6316
|
|
|
6241
6317
|
// src/provenance.ts
|
|
@@ -6624,13 +6700,13 @@ function readingOfCall(call, declaration, names, options) {
|
|
|
6624
6700
|
}
|
|
6625
6701
|
return writtenReading2(status, rangeOf(argument));
|
|
6626
6702
|
}
|
|
6627
|
-
function
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6703
|
+
function withEffectsThatRun(branch, body) {
|
|
6704
|
+
const extra = effectsFiringOn(body.extraEffects ?? [], branch);
|
|
6705
|
+
return {
|
|
6706
|
+
...branch,
|
|
6707
|
+
effects: effectsFiringOn(body.effects, branch),
|
|
6708
|
+
...extra.length === 0 ? {} : { extraEffects: extra }
|
|
6709
|
+
};
|
|
6634
6710
|
}
|
|
6635
6711
|
function conditionOf(condition, inputs) {
|
|
6636
6712
|
return {
|
|
@@ -6690,57 +6766,66 @@ function helperResponse(helper, pattern, facts, boundStatuses = /* @__PURE__ */
|
|
|
6690
6766
|
rangeOf(helper)
|
|
6691
6767
|
);
|
|
6692
6768
|
}
|
|
6693
|
-
function branchOf(outcome, pattern,
|
|
6769
|
+
function branchOf(outcome, pattern, body, options) {
|
|
6694
6770
|
if (outcome.fellThrough === true && options.fallthrough === "handOn") {
|
|
6695
|
-
return handsOnBranch(outcome,
|
|
6771
|
+
return handsOnBranch(outcome, body);
|
|
6696
6772
|
}
|
|
6697
|
-
return
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6773
|
+
return withEffectsThatRun(
|
|
6774
|
+
{
|
|
6775
|
+
conditions: outcome.conditions,
|
|
6776
|
+
terminal: {
|
|
6777
|
+
kind: "response",
|
|
6778
|
+
statusCode: null,
|
|
6779
|
+
body: null,
|
|
6780
|
+
exceptionType: null,
|
|
6781
|
+
message: null,
|
|
6782
|
+
component: null,
|
|
6783
|
+
renderTree: null,
|
|
6784
|
+
delegateTarget: null,
|
|
6785
|
+
emitEvent: null,
|
|
6786
|
+
location: outcome.location
|
|
6787
|
+
},
|
|
6788
|
+
statusCodeReading: {
|
|
6789
|
+
reading: outcome.reading,
|
|
6790
|
+
libraryDefault: pattern.defaultStatusCode
|
|
6791
|
+
},
|
|
6792
|
+
...outcome.body === void 0 ? {} : { bodyShapeReading: { reading: outcome.body } },
|
|
6793
|
+
effects: [],
|
|
6794
|
+
location: outcome.location,
|
|
6795
|
+
...loopAroundOf(outcome),
|
|
6796
|
+
isDefault: outcome.conditions.length === 0
|
|
6714
6797
|
},
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
...extraEffects === void 0 ? {} : { extraEffects },
|
|
6718
|
-
location: outcome.location,
|
|
6719
|
-
isDefault: outcome.conditions.length === 0
|
|
6720
|
-
};
|
|
6798
|
+
body
|
|
6799
|
+
);
|
|
6721
6800
|
}
|
|
6722
|
-
function handsOnBranch(outcome,
|
|
6723
|
-
return
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6801
|
+
function handsOnBranch(outcome, body) {
|
|
6802
|
+
return withEffectsThatRun(
|
|
6803
|
+
{
|
|
6804
|
+
conditions: outcome.conditions,
|
|
6805
|
+
terminal: {
|
|
6806
|
+
kind: "delegate",
|
|
6807
|
+
statusCode: null,
|
|
6808
|
+
body: null,
|
|
6809
|
+
exceptionType: null,
|
|
6810
|
+
message: null,
|
|
6811
|
+
component: null,
|
|
6812
|
+
renderTree: null,
|
|
6813
|
+
delegateTarget: null,
|
|
6814
|
+
emitEvent: null,
|
|
6815
|
+
location: outcome.location
|
|
6816
|
+
},
|
|
6817
|
+
effects: [],
|
|
6818
|
+
location: outcome.location,
|
|
6819
|
+
...loopAroundOf(outcome),
|
|
6820
|
+
isDefault: outcome.conditions.length === 0
|
|
6736
6821
|
},
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
};
|
|
6822
|
+
body
|
|
6823
|
+
);
|
|
6824
|
+
}
|
|
6825
|
+
function loopAroundOf(placed) {
|
|
6826
|
+
return placed.loopAround === void 0 ? {} : { loopAround: placed.loopAround };
|
|
6742
6827
|
}
|
|
6743
|
-
function returnPathBranches(method, effects, exits = [], facts, keepsArms, raises2 = []) {
|
|
6828
|
+
function returnPathBranches(method, effects, exits = [], facts, keepsArms, raises2 = [], endingOfReturn = (_statement, location) => returnTerminal(location)) {
|
|
6744
6829
|
const body = field(method, "body");
|
|
6745
6830
|
if (body === null) {
|
|
6746
6831
|
return null;
|
|
@@ -6753,7 +6838,7 @@ function returnPathBranches(method, effects, exits = [], facts, keepsArms, raise
|
|
|
6753
6838
|
...raises2.map((raise) => raise.call)
|
|
6754
6839
|
];
|
|
6755
6840
|
const lowered = lowerRubyBody(body, terminals, exitCalls);
|
|
6756
|
-
const enumerated =
|
|
6841
|
+
const enumerated = enumerateOrDegrade(
|
|
6757
6842
|
{
|
|
6758
6843
|
statements: lowered.statements,
|
|
6759
6844
|
terminalsByStmt: lowered.terminalsByStmt,
|
|
@@ -6767,30 +6852,42 @@ function returnPathBranches(method, effects, exits = [], facts, keepsArms, raise
|
|
|
6767
6852
|
facts
|
|
6768
6853
|
);
|
|
6769
6854
|
const branches = [];
|
|
6770
|
-
const push = (paths, terminal) => {
|
|
6855
|
+
const push = (paths, terminal, at) => {
|
|
6856
|
+
const placement = at === null ? void 0 : enumerated.placement.get(at);
|
|
6771
6857
|
for (const path7 of paths) {
|
|
6772
6858
|
const conditions = path7.map(
|
|
6773
6859
|
(condition) => conditionOf(condition, inputs)
|
|
6774
6860
|
);
|
|
6775
|
-
branches.push(
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6861
|
+
branches.push(
|
|
6862
|
+
withEffectsThatRun(
|
|
6863
|
+
{
|
|
6864
|
+
conditions,
|
|
6865
|
+
terminal,
|
|
6866
|
+
effects: [],
|
|
6867
|
+
location: terminal.location,
|
|
6868
|
+
...loopAroundOf({ loopAround: placement?.loopAround }),
|
|
6869
|
+
isDefault: conditions.length === 0
|
|
6870
|
+
},
|
|
6871
|
+
{ effects }
|
|
6872
|
+
)
|
|
6873
|
+
);
|
|
6782
6874
|
}
|
|
6783
6875
|
};
|
|
6784
6876
|
for (const statement of returns) {
|
|
6785
6877
|
push(
|
|
6786
6878
|
enumerated.byTerminal.get(statement) ?? [],
|
|
6787
|
-
|
|
6879
|
+
endingOfReturn(statement, rangeOf(statement)),
|
|
6880
|
+
statement
|
|
6788
6881
|
);
|
|
6789
6882
|
}
|
|
6790
6883
|
for (const ending of [...exits, ...raises2]) {
|
|
6791
|
-
push(
|
|
6884
|
+
push(
|
|
6885
|
+
enumerated.byTerminal.get(ending.call) ?? [],
|
|
6886
|
+
ending.terminal,
|
|
6887
|
+
ending.call
|
|
6888
|
+
);
|
|
6792
6889
|
}
|
|
6793
|
-
push(enumerated.fallthrough,
|
|
6890
|
+
push(enumerated.fallthrough, endingOfReturn(null, rangeOf(method)), null);
|
|
6794
6891
|
return branches.length === 0 ? null : branches;
|
|
6795
6892
|
}
|
|
6796
6893
|
function returnTerminal(location) {
|
|
@@ -6831,7 +6928,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
6831
6928
|
(node) => lowered.terminalHome.has(node) && collectResponseCalls(node, responders, []).length === 0
|
|
6832
6929
|
);
|
|
6833
6930
|
const terminals = [...responses, ...bareReturns];
|
|
6834
|
-
const enumerated =
|
|
6931
|
+
const enumerated = enumerateOrDegrade(
|
|
6835
6932
|
{
|
|
6836
6933
|
statements: lowered.statements,
|
|
6837
6934
|
terminalsByStmt: lowered.terminalsByStmt
|
|
@@ -6850,12 +6947,14 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
6850
6947
|
const responder = responderOf(terminal, responders);
|
|
6851
6948
|
const reading = responder === void 0 ? absentReading2 : readingOfResponder(terminal, responder, statusNames, options);
|
|
6852
6949
|
const body2 = responder?.kind === "declared" ? bodyReadingOfCall(terminal, responder.declaration, options.facts) : void 0;
|
|
6950
|
+
const loopAround = enumerated.placement.get(terminal)?.loopAround;
|
|
6853
6951
|
for (const path7 of enumerated.byTerminal.get(terminal) ?? []) {
|
|
6854
6952
|
outcomes.push({
|
|
6855
6953
|
conditions: conditionsOf(path7),
|
|
6856
6954
|
reading,
|
|
6857
6955
|
...body2 === void 0 ? {} : { body: body2 },
|
|
6858
6956
|
location: rangeOf(terminal),
|
|
6957
|
+
loopAround,
|
|
6859
6958
|
// A bare `return` wrote no response, so a filter that takes it
|
|
6860
6959
|
// hands the request on the same as one that reaches its end.
|
|
6861
6960
|
...responder === void 0 ? { fellThrough: true } : {}
|
|
@@ -6871,6 +6970,7 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
6871
6970
|
});
|
|
6872
6971
|
}
|
|
6873
6972
|
const read = options.raises;
|
|
6973
|
+
const placed = { effects, extraEffects };
|
|
6874
6974
|
const raised = raises2.flatMap((call) => {
|
|
6875
6975
|
const terminal = raisedTerminal(
|
|
6876
6976
|
pattern,
|
|
@@ -6878,25 +6978,26 @@ function responseBranches(method, pattern, effects, extraEffects, options = {})
|
|
|
6878
6978
|
read?.handlers ?? [],
|
|
6879
6979
|
rangeOf(call)
|
|
6880
6980
|
);
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6981
|
+
const loopAround = enumerated.placement.get(call)?.loopAround;
|
|
6982
|
+
return (enumerated.byTerminal.get(call) ?? []).map(
|
|
6983
|
+
(path7) => withEffectsThatRun(
|
|
6984
|
+
{
|
|
6985
|
+
conditions: conditionsOf(path7),
|
|
6986
|
+
terminal,
|
|
6987
|
+
effects: [],
|
|
6988
|
+
location: terminal.location,
|
|
6989
|
+
...loopAroundOf({ loopAround }),
|
|
6990
|
+
isDefault: false
|
|
6991
|
+
},
|
|
6992
|
+
placed
|
|
6993
|
+
)
|
|
6994
|
+
);
|
|
6892
6995
|
});
|
|
6893
6996
|
if (outcomes.length === 0 && raised.length === 0) {
|
|
6894
6997
|
return null;
|
|
6895
6998
|
}
|
|
6896
6999
|
return [
|
|
6897
|
-
...outcomes.map(
|
|
6898
|
-
(outcome) => branchOf(outcome, pattern, effects, extraEffects, options)
|
|
6899
|
-
),
|
|
7000
|
+
...outcomes.map((outcome) => branchOf(outcome, pattern, placed, options)),
|
|
6900
7001
|
...raised
|
|
6901
7002
|
];
|
|
6902
7003
|
}
|
|
@@ -7164,7 +7265,7 @@ function callerBranches(method, reading) {
|
|
|
7164
7265
|
const keepsArms = (condition) => testsStatus(predicateOf(condition), reading.statusMembers);
|
|
7165
7266
|
const found = returnPathBranches(
|
|
7166
7267
|
method,
|
|
7167
|
-
|
|
7268
|
+
sites,
|
|
7168
7269
|
[],
|
|
7169
7270
|
reading.facts,
|
|
7170
7271
|
keepsArms,
|
|
@@ -7623,7 +7724,7 @@ function envReadEffects(root, facts) {
|
|
|
7623
7724
|
at: (node) => {
|
|
7624
7725
|
const read = envReadAt(node);
|
|
7625
7726
|
if (read !== null) {
|
|
7626
|
-
slots.push({ read });
|
|
7727
|
+
slots.push({ read, at: node });
|
|
7627
7728
|
return;
|
|
7628
7729
|
}
|
|
7629
7730
|
if (helpers !== null && node.type === "call") {
|
|
@@ -7636,9 +7737,12 @@ function envReadEffects(root, facts) {
|
|
|
7636
7737
|
const throughHelpers = helpers === null ? new NodeMap() : helperReads(calls, helpers);
|
|
7637
7738
|
return slots.flatMap((slot) => {
|
|
7638
7739
|
if ("read" in slot) {
|
|
7639
|
-
return [configReadEffect(slot.read)];
|
|
7740
|
+
return [{ effect: configReadEffect(slot.read, slot.at), at: slot.at }];
|
|
7640
7741
|
}
|
|
7641
|
-
return (throughHelpers.get(slot.call) ?? []).map(
|
|
7742
|
+
return (throughHelpers.get(slot.call) ?? []).map((read) => ({
|
|
7743
|
+
effect: configReadEffect(read, slot.call),
|
|
7744
|
+
at: slot.call
|
|
7745
|
+
}));
|
|
7642
7746
|
});
|
|
7643
7747
|
}
|
|
7644
7748
|
function helperFactsOf(facts) {
|
|
@@ -7794,16 +7898,17 @@ function readsAtCall(db, call, args) {
|
|
|
7794
7898
|
}
|
|
7795
7899
|
return [...byName].map(([name, defaulted]) => ({ name, defaulted }));
|
|
7796
7900
|
}
|
|
7797
|
-
function configReadEffect(read) {
|
|
7901
|
+
function configReadEffect(read, at) {
|
|
7798
7902
|
return {
|
|
7799
7903
|
type: "interaction",
|
|
7800
7904
|
binding: runtimeConfigBinding({ recognition: RUBY_ENV_RECOGNITION }),
|
|
7801
|
-
callee: `ENV["${read.name}"]`,
|
|
7905
|
+
callee: read.name === null ? at.text : `ENV["${read.name}"]`,
|
|
7802
7906
|
interaction: {
|
|
7803
7907
|
class: "config-read",
|
|
7804
7908
|
name: read.name,
|
|
7805
7909
|
defaulted: read.defaulted
|
|
7806
|
-
}
|
|
7910
|
+
},
|
|
7911
|
+
location: rangeOf(at)
|
|
7807
7912
|
};
|
|
7808
7913
|
}
|
|
7809
7914
|
function envReadAt(node) {
|
|
@@ -7811,8 +7916,7 @@ function envReadAt(node) {
|
|
|
7811
7916
|
if (site === null) {
|
|
7812
7917
|
return null;
|
|
7813
7918
|
}
|
|
7814
|
-
|
|
7815
|
-
return name === null ? null : { name, defaulted: site.defaulted };
|
|
7919
|
+
return { name: stringLiteralValue(site.name), defaulted: site.defaulted };
|
|
7816
7920
|
}
|
|
7817
7921
|
function envSiteAt(node) {
|
|
7818
7922
|
const spelling = envSpellingAt(node);
|
|
@@ -8008,25 +8112,27 @@ function filterUnit(filter, pattern, displayPath, body, reading = {}) {
|
|
|
8008
8112
|
};
|
|
8009
8113
|
}
|
|
8010
8114
|
function handsOn(range, body) {
|
|
8011
|
-
return
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8115
|
+
return withEffectsThatRun(
|
|
8116
|
+
{
|
|
8117
|
+
conditions: [],
|
|
8118
|
+
terminal: {
|
|
8119
|
+
kind: "delegate",
|
|
8120
|
+
statusCode: null,
|
|
8121
|
+
body: null,
|
|
8122
|
+
exceptionType: null,
|
|
8123
|
+
message: null,
|
|
8124
|
+
component: null,
|
|
8125
|
+
renderTree: null,
|
|
8126
|
+
delegateTarget: null,
|
|
8127
|
+
emitEvent: null,
|
|
8128
|
+
location: range
|
|
8129
|
+
},
|
|
8130
|
+
effects: [],
|
|
8131
|
+
location: range,
|
|
8132
|
+
isDefault: true
|
|
8024
8133
|
},
|
|
8025
|
-
effects: body.effects ?? [],
|
|
8026
|
-
|
|
8027
|
-
location: range,
|
|
8028
|
-
isDefault: true
|
|
8029
|
-
};
|
|
8134
|
+
{ effects: body.effects ?? [], extraEffects: body.extraEffects }
|
|
8135
|
+
);
|
|
8030
8136
|
}
|
|
8031
8137
|
function calledName(statement) {
|
|
8032
8138
|
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
@@ -8163,34 +8269,214 @@ function actionsUnder(node, facts) {
|
|
|
8163
8269
|
return found === null ? null : new Set(found);
|
|
8164
8270
|
}
|
|
8165
8271
|
|
|
8166
|
-
// src/
|
|
8167
|
-
import {
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
[
|
|
8175
|
-
]
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
}
|
|
8192
|
-
}
|
|
8193
|
-
|
|
8272
|
+
// src/resolverHooks.ts
|
|
8273
|
+
import {
|
|
8274
|
+
SKIP_CHILDREN as SKIP_CHILDREN3,
|
|
8275
|
+
unreadableReading as unreadableReading2,
|
|
8276
|
+
walkDescendants as walkDescendants3
|
|
8277
|
+
} from "@suss/extractor";
|
|
8278
|
+
import { constantOf as constantOf4 } from "@suss/values";
|
|
8279
|
+
function hooksRun(ancestry, hooks, read) {
|
|
8280
|
+
const definitions = [];
|
|
8281
|
+
const unsettled = [];
|
|
8282
|
+
for (const name of hooks.methods) {
|
|
8283
|
+
const found = definitionsRun(ancestry, name, read);
|
|
8284
|
+
definitions.push(...found.definitions);
|
|
8285
|
+
if (found.unsettled !== null) {
|
|
8286
|
+
unsettled.push({ name, reason: found.unsettled });
|
|
8287
|
+
}
|
|
8288
|
+
}
|
|
8289
|
+
return { definitions, unsettled };
|
|
8290
|
+
}
|
|
8291
|
+
function definitionsRun(ancestry, name, read) {
|
|
8292
|
+
const definitions = [];
|
|
8293
|
+
let rest = ancestry;
|
|
8294
|
+
while (rest.length > 0) {
|
|
8295
|
+
const lookup = methodInAncestry(rest, name, read);
|
|
8296
|
+
if (lookup.type !== "found") {
|
|
8297
|
+
return { definitions, unsettled: unsettledReason(lookup) };
|
|
8298
|
+
}
|
|
8299
|
+
definitions.push({ name, method: lookup.method, block: lookup.block });
|
|
8300
|
+
if (!callsSuper(lookup.method)) {
|
|
8301
|
+
break;
|
|
8302
|
+
}
|
|
8303
|
+
const at = rest.findIndex(
|
|
8304
|
+
(entry) => entry.type === "bodies" && entry.blocks.includes(lookup.block)
|
|
8305
|
+
);
|
|
8306
|
+
rest = rest.slice(Math.max(at, 0) + 1);
|
|
8307
|
+
}
|
|
8308
|
+
return { definitions, unsettled: null };
|
|
8309
|
+
}
|
|
8310
|
+
function unsettledReason(lookup) {
|
|
8311
|
+
if (lookup.type !== "unsettled") {
|
|
8312
|
+
return null;
|
|
8313
|
+
}
|
|
8314
|
+
return lookup.cause === "dynamicDefine" || lookup.definedLater === true ? lookup.reason : null;
|
|
8315
|
+
}
|
|
8316
|
+
function callsSuper(method) {
|
|
8317
|
+
let found = false;
|
|
8318
|
+
walkDescendants3(method, null, children, {
|
|
8319
|
+
at: (node) => {
|
|
8320
|
+
found ||= isSuper(node);
|
|
8321
|
+
},
|
|
8322
|
+
into: (node) => node !== method && METHOD_TYPES.has(node.type) ? SKIP_CHILDREN3 : null
|
|
8323
|
+
});
|
|
8324
|
+
return found;
|
|
8325
|
+
}
|
|
8326
|
+
function isSuper(node) {
|
|
8327
|
+
return node.type === "super" || node.type === "call" && field(node, "method")?.type === "super";
|
|
8328
|
+
}
|
|
8329
|
+
function hookUnit(hook, input) {
|
|
8330
|
+
const { body, facts } = input;
|
|
8331
|
+
const range = rangeOf(hook.method);
|
|
8332
|
+
const effects = body.effects ?? [];
|
|
8333
|
+
const branches = (returnPathBranches(
|
|
8334
|
+
hook.method,
|
|
8335
|
+
effects,
|
|
8336
|
+
[],
|
|
8337
|
+
facts,
|
|
8338
|
+
void 0,
|
|
8339
|
+
raiseEndings(hook.method),
|
|
8340
|
+
(statement, location) => hookEnding(hook.method, statement, location, input)
|
|
8341
|
+
) ?? [emptyBodyBranch(range)]).map(
|
|
8342
|
+
(branch) => withEffectsThatRun(branch, {
|
|
8343
|
+
effects,
|
|
8344
|
+
extraEffects: body.extraEffects
|
|
8345
|
+
})
|
|
8346
|
+
);
|
|
8347
|
+
return {
|
|
8348
|
+
identity: {
|
|
8349
|
+
name: hook.name,
|
|
8350
|
+
nameKind: "binding",
|
|
8351
|
+
kind: "middleware",
|
|
8352
|
+
file: input.displayPath,
|
|
8353
|
+
range,
|
|
8354
|
+
span: spanOf(hook.method),
|
|
8355
|
+
exportName: hook.name,
|
|
8356
|
+
exportPath: [hook.block.info.qualifiedName, hook.name]
|
|
8357
|
+
},
|
|
8358
|
+
boundaryBinding: null,
|
|
8359
|
+
parameters: [],
|
|
8360
|
+
branches: withSlotSources(branches, body.provenance),
|
|
8361
|
+
bodyContent: body.bodyContent ?? "absent",
|
|
8362
|
+
dependencyCalls: [],
|
|
8363
|
+
declaredContract: null
|
|
8364
|
+
};
|
|
8365
|
+
}
|
|
8366
|
+
function hookReference(hook, displayPath) {
|
|
8367
|
+
return {
|
|
8368
|
+
file: displayPath,
|
|
8369
|
+
name: hook.name,
|
|
8370
|
+
line: rangeOf(hook.method).start
|
|
8371
|
+
};
|
|
8372
|
+
}
|
|
8373
|
+
function unsettledHookReading(className, unsettled, range) {
|
|
8374
|
+
return unreadableReading2(
|
|
8375
|
+
`This field's ${className} could be stopped by a ${unsettled.name} hook ${unsettled.reason}, so whether one runs was not settled here`,
|
|
8376
|
+
range
|
|
8377
|
+
);
|
|
8378
|
+
}
|
|
8379
|
+
function hookEnding(method, statement, location, input) {
|
|
8380
|
+
const values = statement === null ? lastStatement(method) : returnedValues(statement);
|
|
8381
|
+
return stopsTheField(values, input) ? endingTerminal("return", location) : goOn(location);
|
|
8382
|
+
}
|
|
8383
|
+
function lastStatement(method) {
|
|
8384
|
+
const body = field(method, "body");
|
|
8385
|
+
const last = body === null ? void 0 : bodyStatements(body).at(-1);
|
|
8386
|
+
return last === void 0 ? [] : [last];
|
|
8387
|
+
}
|
|
8388
|
+
function returnedValues(statement) {
|
|
8389
|
+
const list = children(statement)[0];
|
|
8390
|
+
if (list === void 0) {
|
|
8391
|
+
return [];
|
|
8392
|
+
}
|
|
8393
|
+
return list.type === "argument_list" ? children(list) : [list];
|
|
8394
|
+
}
|
|
8395
|
+
function stopsTheField(values, input) {
|
|
8396
|
+
const first = values[0];
|
|
8397
|
+
if (first === void 0) {
|
|
8398
|
+
return true;
|
|
8399
|
+
}
|
|
8400
|
+
const listHead = input.hooks.answerIsListHead === true;
|
|
8401
|
+
if (values.length > 1) {
|
|
8402
|
+
return listHead && isFalsy(evaluatedValue(first, input.facts));
|
|
8403
|
+
}
|
|
8404
|
+
const value = evaluatedValue(first, input.facts);
|
|
8405
|
+
if (value.kind !== "sequence") {
|
|
8406
|
+
return isFalsy(value);
|
|
8407
|
+
}
|
|
8408
|
+
const head = value.items[0];
|
|
8409
|
+
return listHead && head !== void 0 && isFalsy(head.value);
|
|
8410
|
+
}
|
|
8411
|
+
function isFalsy(value) {
|
|
8412
|
+
const settled = constantOf4(value);
|
|
8413
|
+
return settled === false || settled === null;
|
|
8414
|
+
}
|
|
8415
|
+
function emptyBodyBranch(location) {
|
|
8416
|
+
return {
|
|
8417
|
+
conditions: [],
|
|
8418
|
+
terminal: endingTerminal("return", location),
|
|
8419
|
+
effects: [],
|
|
8420
|
+
location,
|
|
8421
|
+
isDefault: true
|
|
8422
|
+
};
|
|
8423
|
+
}
|
|
8424
|
+
function goOn(location) {
|
|
8425
|
+
return endingTerminal("delegate", location);
|
|
8426
|
+
}
|
|
8427
|
+
function endingTerminal(kind, location) {
|
|
8428
|
+
return {
|
|
8429
|
+
kind,
|
|
8430
|
+
statusCode: null,
|
|
8431
|
+
body: null,
|
|
8432
|
+
exceptionType: null,
|
|
8433
|
+
message: null,
|
|
8434
|
+
component: null,
|
|
8435
|
+
renderTree: null,
|
|
8436
|
+
delegateTarget: null,
|
|
8437
|
+
emitEvent: null,
|
|
8438
|
+
location
|
|
8439
|
+
};
|
|
8440
|
+
}
|
|
8441
|
+
|
|
8442
|
+
// src/streamWrites.ts
|
|
8443
|
+
import { ioBinding } from "@suss/behavioral-ir";
|
|
8444
|
+
import {
|
|
8445
|
+
callSiteKey,
|
|
8446
|
+
numberRepeatedCallSites,
|
|
8447
|
+
SKIP_CHILDREN as SKIP_CHILDREN4,
|
|
8448
|
+
walkDescendants as walkDescendants4
|
|
8449
|
+
} from "@suss/extractor";
|
|
8450
|
+
var RUBY_STREAM_RECOGNITION = "ruby-stdlib";
|
|
8451
|
+
var KERNEL_WRITES = /* @__PURE__ */ new Map([
|
|
8452
|
+
["puts", "stdout"],
|
|
8453
|
+
["print", "stdout"],
|
|
8454
|
+
["warn", "stderr"],
|
|
8455
|
+
["abort", "stderr"]
|
|
8456
|
+
]);
|
|
8457
|
+
var STREAM_RECEIVERS = /* @__PURE__ */ new Map([
|
|
8458
|
+
["$stdout", "stdout"],
|
|
8459
|
+
["STDOUT", "stdout"],
|
|
8460
|
+
["$stderr", "stderr"],
|
|
8461
|
+
["STDERR", "stderr"]
|
|
8462
|
+
]);
|
|
8463
|
+
var STREAM_METHODS = /* @__PURE__ */ new Set(["puts", "print", "write", "<<"]);
|
|
8464
|
+
var DEFERRED_BODY_TYPES2 = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
8465
|
+
function streamWriteEffects(root) {
|
|
8466
|
+
return numberRepeatedCallSites(
|
|
8467
|
+
writesUnder2(root).map((write) => [write])
|
|
8468
|
+
).flat();
|
|
8469
|
+
}
|
|
8470
|
+
function writesUnder2(root) {
|
|
8471
|
+
const found = [];
|
|
8472
|
+
walkDescendants4(root, null, children, {
|
|
8473
|
+
at: (node) => {
|
|
8474
|
+
const write = node.type === "call" ? streamWriteAt(node) : null;
|
|
8475
|
+
if (write !== null) {
|
|
8476
|
+
found.push({ effect: write, at: node });
|
|
8477
|
+
}
|
|
8478
|
+
},
|
|
8479
|
+
into: (node) => DEFERRED_BODY_TYPES2.has(node.type) ? SKIP_CHILDREN4 : null
|
|
8194
8480
|
});
|
|
8195
8481
|
return found;
|
|
8196
8482
|
}
|
|
@@ -8219,13 +8505,14 @@ function streamWriteAt(call) {
|
|
|
8219
8505
|
function writeEffect(call, target, args) {
|
|
8220
8506
|
const receiver = field(call, "receiver");
|
|
8221
8507
|
const method = field(call, "method")?.text ?? "";
|
|
8222
|
-
|
|
8508
|
+
const effect = {
|
|
8223
8509
|
type: "interaction",
|
|
8224
8510
|
binding: ioBinding({ recognition: RUBY_STREAM_RECOGNITION, target }),
|
|
8225
8511
|
callee: receiver === null ? method : `${receiver.text}.${method}`,
|
|
8226
|
-
|
|
8227
|
-
|
|
8512
|
+
interaction: { class: "stream-write", ...payloadOf(args) },
|
|
8513
|
+
location: rangeOf(call)
|
|
8228
8514
|
};
|
|
8515
|
+
return { ...effect, groupId: callSiteKey(effect) };
|
|
8229
8516
|
}
|
|
8230
8517
|
function payloadOf(args) {
|
|
8231
8518
|
const only = args[0];
|
|
@@ -9305,16 +9592,19 @@ function createFileCache(parse, readFile) {
|
|
|
9305
9592
|
}
|
|
9306
9593
|
};
|
|
9307
9594
|
}
|
|
9308
|
-
function fieldReadContext(pattern, cache, fileBlocks,
|
|
9595
|
+
function fieldReadContext(pattern, cache, fileBlocks, options) {
|
|
9596
|
+
const facts = options.facts;
|
|
9597
|
+
const parsedFile = (absPath) => cache.get(absPath);
|
|
9309
9598
|
return {
|
|
9310
9599
|
pattern,
|
|
9311
9600
|
cache,
|
|
9312
|
-
bodyRead,
|
|
9601
|
+
bodyRead: options,
|
|
9313
9602
|
facts,
|
|
9603
|
+
file: options.absoluteFile ?? options.filePath,
|
|
9314
9604
|
read: {
|
|
9315
9605
|
facts,
|
|
9316
|
-
bodyBlocks:
|
|
9317
|
-
dynamicNames:
|
|
9606
|
+
bodyBlocks: options.bodyBlocks,
|
|
9607
|
+
dynamicNames: options.dynamicNames
|
|
9318
9608
|
},
|
|
9319
9609
|
lookup: {
|
|
9320
9610
|
root: pattern.root,
|
|
@@ -9323,8 +9613,9 @@ function fieldReadContext(pattern, cache, fileBlocks, bodyRead, facts) {
|
|
|
9323
9613
|
acronyms: pattern.acronyms ?? [],
|
|
9324
9614
|
ancestryRootClassNames: pattern.ancestryRootClassNames,
|
|
9325
9615
|
constantFiles: cache.constantFiles,
|
|
9326
|
-
parsedFile
|
|
9327
|
-
localDefinition: (name) => sameFileBlocks(name, fileBlocks)
|
|
9616
|
+
parsedFile,
|
|
9617
|
+
localDefinition: (name) => sameFileBlocks(name, fileBlocks),
|
|
9618
|
+
...facts === void 0 ? {} : { definedInRun: classesDeclaredInRun(facts, parsedFile) }
|
|
9328
9619
|
}
|
|
9329
9620
|
};
|
|
9330
9621
|
}
|
|
@@ -9433,13 +9724,7 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
|
|
|
9433
9724
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
9434
9725
|
return [];
|
|
9435
9726
|
}
|
|
9436
|
-
const ctx = fieldReadContext(
|
|
9437
|
-
pattern,
|
|
9438
|
-
options.cache,
|
|
9439
|
-
fileBlocks,
|
|
9440
|
-
options,
|
|
9441
|
-
options.facts
|
|
9442
|
-
);
|
|
9727
|
+
const ctx = fieldReadContext(pattern, options.cache, fileBlocks, options);
|
|
9443
9728
|
const ancestry = await ancestryOf(info.qualifiedName, ownBlocks, ctx.lookup);
|
|
9444
9729
|
if (!reachesConfiguredBase(ancestry, info.qualifiedName, pattern.baseClassNames)) {
|
|
9445
9730
|
return [];
|
|
@@ -9460,13 +9745,47 @@ async function graphqlObjectFieldUnits(pattern, pack, info, ownBlocks, fileBlock
|
|
|
9460
9745
|
declsByName.set(decl.fieldName, decl);
|
|
9461
9746
|
}
|
|
9462
9747
|
}
|
|
9463
|
-
|
|
9464
|
-
|
|
9748
|
+
const decls = [...declsByName.values()];
|
|
9749
|
+
const fields = decls.map((decl) => {
|
|
9750
|
+
const raw = buildFieldUnit(pack, typeName, decl, options);
|
|
9465
9751
|
if (decl.body.reachSeed !== void 0) {
|
|
9466
9752
|
options.onReachSeed?.(raw, decl.body.reachSeed);
|
|
9467
9753
|
}
|
|
9468
9754
|
return raw;
|
|
9469
9755
|
});
|
|
9756
|
+
return [...fields, ...hookUnitsOf(decls, pattern, options)];
|
|
9757
|
+
}
|
|
9758
|
+
function hookUnitsOf(decls, pattern, options) {
|
|
9759
|
+
const hooks = pattern.resolverHooks;
|
|
9760
|
+
if (hooks === void 0) {
|
|
9761
|
+
return [];
|
|
9762
|
+
}
|
|
9763
|
+
const units = /* @__PURE__ */ new Map();
|
|
9764
|
+
for (const hook of decls.flatMap((decl) => decl.hooks)) {
|
|
9765
|
+
const key = nodeId(hook.block.file, hook.method);
|
|
9766
|
+
if (units.has(key)) {
|
|
9767
|
+
continue;
|
|
9768
|
+
}
|
|
9769
|
+
const raw = hookUnit(hook, {
|
|
9770
|
+
hooks,
|
|
9771
|
+
body: bodyOfMethod(hook.method, hook.block.file, options),
|
|
9772
|
+
displayPath: displayPathIn(options, hook.block.file),
|
|
9773
|
+
facts: options.facts
|
|
9774
|
+
});
|
|
9775
|
+
units.set(key, raw);
|
|
9776
|
+
options.onReachSeed?.(raw, {
|
|
9777
|
+
file: hook.block.file,
|
|
9778
|
+
node: hook.method,
|
|
9779
|
+
enclosingQualifiedName: hook.block.info.qualifiedName
|
|
9780
|
+
});
|
|
9781
|
+
}
|
|
9782
|
+
return [...units.values()];
|
|
9783
|
+
}
|
|
9784
|
+
function displayPathIn(options, file) {
|
|
9785
|
+
if (file === (options.absoluteFile ?? options.filePath)) {
|
|
9786
|
+
return options.filePath;
|
|
9787
|
+
}
|
|
9788
|
+
return options.displayPathOf?.(file) ?? file;
|
|
9470
9789
|
}
|
|
9471
9790
|
async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks, options) {
|
|
9472
9791
|
if (info.bodyNode === null || pattern.baseClassNames.includes(info.qualifiedName)) {
|
|
@@ -9543,7 +9862,7 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
9543
9862
|
raised: raisedStatusBranches(
|
|
9544
9863
|
pattern,
|
|
9545
9864
|
options.storage?.patterns ?? [],
|
|
9546
|
-
body.extraEffects,
|
|
9865
|
+
everyEffect(body.extraEffects),
|
|
9547
9866
|
"eachRoute",
|
|
9548
9867
|
rangeOf(filter.method)
|
|
9549
9868
|
)
|
|
@@ -9576,10 +9895,7 @@ async function controllerActionUnits(pattern, pack, info, ownBlocks, fileBlocks,
|
|
|
9576
9895
|
actionName,
|
|
9577
9896
|
route,
|
|
9578
9897
|
method,
|
|
9579
|
-
{
|
|
9580
|
-
display: block.file === (options.absoluteFile ?? options.filePath) ? options.filePath : options.displayPathOf?.(block.file) ?? block.file,
|
|
9581
|
-
absolute: block.file
|
|
9582
|
-
},
|
|
9898
|
+
{ display: displayPathIn(options, block.file), absolute: block.file },
|
|
9583
9899
|
options,
|
|
9584
9900
|
around,
|
|
9585
9901
|
respondingHelper,
|
|
@@ -9748,8 +10064,8 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
9748
10064
|
reading: absentReading3,
|
|
9749
10065
|
libraryDefault: pattern.defaultStatusCode
|
|
9750
10066
|
},
|
|
9751
|
-
effects: body.effects
|
|
9752
|
-
...body.extraEffects === void 0 ? {} : { extraEffects: body.extraEffects },
|
|
10067
|
+
effects: everyEffect(body.effects),
|
|
10068
|
+
...body.extraEffects === void 0 ? {} : { extraEffects: everyEffect(body.extraEffects) },
|
|
9753
10069
|
location: range,
|
|
9754
10070
|
isDefault: true
|
|
9755
10071
|
}
|
|
@@ -9759,7 +10075,7 @@ function buildControllerActionUnit(pack, pattern, controllerQualifiedName, actio
|
|
|
9759
10075
|
raisedStatusBranches(
|
|
9760
10076
|
pattern,
|
|
9761
10077
|
bodyRead.storage?.patterns ?? [],
|
|
9762
|
-
body.extraEffects,
|
|
10078
|
+
everyEffect(body.extraEffects),
|
|
9763
10079
|
raises2.handlers,
|
|
9764
10080
|
range
|
|
9765
10081
|
)
|
|
@@ -9804,7 +10120,7 @@ function routingGapUnit(pattern, gaps) {
|
|
|
9804
10120
|
isDefault: true
|
|
9805
10121
|
}
|
|
9806
10122
|
],
|
|
9807
|
-
readings: gaps.map((gap) =>
|
|
10123
|
+
readings: gaps.map((gap) => unreadableReading3(gap, range)),
|
|
9808
10124
|
dependencyCalls: [],
|
|
9809
10125
|
declaredContract: null
|
|
9810
10126
|
};
|
|
@@ -9838,15 +10154,18 @@ function branchesFor(body, range) {
|
|
|
9838
10154
|
emitEvent: null,
|
|
9839
10155
|
location: range
|
|
9840
10156
|
},
|
|
9841
|
-
effects: body.effects
|
|
9842
|
-
...body.extraEffects === void 0 ? {} : { extraEffects: body.extraEffects },
|
|
10157
|
+
effects: everyEffect(body.effects),
|
|
10158
|
+
...body.extraEffects === void 0 ? {} : { extraEffects: everyEffect(body.extraEffects) },
|
|
9843
10159
|
location: range,
|
|
9844
10160
|
isDefault: true
|
|
9845
10161
|
};
|
|
9846
10162
|
return withSlotSources([branch], body.provenance);
|
|
9847
10163
|
}
|
|
10164
|
+
function everyEffect(placed) {
|
|
10165
|
+
return (placed ?? []).map((one) => one.effect);
|
|
10166
|
+
}
|
|
9848
10167
|
function bodyOfMethod(method, file, bodyRead = {}) {
|
|
9849
|
-
const effects =
|
|
10168
|
+
const effects = invocationSites(
|
|
9850
10169
|
method,
|
|
9851
10170
|
bodyRead.inheritedMethods,
|
|
9852
10171
|
EVERY_ARGLESS_CALL,
|
|
@@ -9855,7 +10174,7 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
9855
10174
|
const storage = bodyRead.storage;
|
|
9856
10175
|
const facts = bodyRead.facts;
|
|
9857
10176
|
const database = storage === void 0 ? { effects: [], slots: [] } : methodStorage(method, file, storage);
|
|
9858
|
-
const
|
|
10177
|
+
const found = [
|
|
9859
10178
|
...envReadEffects(
|
|
9860
10179
|
method,
|
|
9861
10180
|
facts === void 0 ? void 0 : { db: facts, file }
|
|
@@ -9863,6 +10182,11 @@ function bodyOfMethod(method, file, bodyRead = {}) {
|
|
|
9863
10182
|
...database.effects,
|
|
9864
10183
|
...streamWriteEffects(method)
|
|
9865
10184
|
];
|
|
10185
|
+
const siteOf3 = effectSitesIn(
|
|
10186
|
+
methodBody(method),
|
|
10187
|
+
found.map((one) => one.at)
|
|
10188
|
+
);
|
|
10189
|
+
const extra = found.map(({ effect, at }) => ({ effect, site: siteOf3(at) }));
|
|
9866
10190
|
const provenance = database.slots.length === 0 || storage === void 0 ? [] : slotProvenance(database.slots, { facts: storage.facts, unit: method });
|
|
9867
10191
|
return {
|
|
9868
10192
|
bodyContent: methodHasStatements(method) ? "statements" : "empty",
|
|
@@ -9877,7 +10201,7 @@ var NO_METHOD_BEHIND_IT = {
|
|
|
9877
10201
|
readings: []
|
|
9878
10202
|
};
|
|
9879
10203
|
function methodNotSettled(reason, range) {
|
|
9880
|
-
return { readings: [
|
|
10204
|
+
return { readings: [unreadableReading3(reason, range)] };
|
|
9881
10205
|
}
|
|
9882
10206
|
function bodyFromLookup(found, range, subject, nothingThere, bodyRead) {
|
|
9883
10207
|
const table = {
|
|
@@ -9926,7 +10250,8 @@ async function readFieldCall(stmt, scope, ctx, ancestry) {
|
|
|
9926
10250
|
namedOnTheWire: true,
|
|
9927
10251
|
node: stmt,
|
|
9928
10252
|
contract: read.contract,
|
|
9929
|
-
body: read.body
|
|
10253
|
+
body: read.body,
|
|
10254
|
+
hooks: read.hooks ?? []
|
|
9930
10255
|
};
|
|
9931
10256
|
}
|
|
9932
10257
|
function computedNameDeclaration(stmt, nameArg) {
|
|
@@ -9936,9 +10261,10 @@ function computedNameDeclaration(stmt, nameArg) {
|
|
|
9936
10261
|
namedOnTheWire: false,
|
|
9937
10262
|
node: stmt,
|
|
9938
10263
|
contract: null,
|
|
10264
|
+
hooks: [],
|
|
9939
10265
|
body: {
|
|
9940
10266
|
readings: [
|
|
9941
|
-
|
|
10267
|
+
unreadableReading3(
|
|
9942
10268
|
`This field is named by ${nameArg.text}, which is worked out when the class body runs, so the name the schema exposes it under was not read here`,
|
|
9943
10269
|
range
|
|
9944
10270
|
)
|
|
@@ -9962,14 +10288,38 @@ async function readFieldShape(symbol, callArgs, scope, ctx, ancestry, range) {
|
|
|
9962
10288
|
}
|
|
9963
10289
|
return {
|
|
9964
10290
|
contract: literalContract(callArgs, scope, ctx),
|
|
9965
|
-
body:
|
|
10291
|
+
body: methodBehindField(symbol, callArgs, ctx, ancestry, range)
|
|
10292
|
+
};
|
|
10293
|
+
}
|
|
10294
|
+
function methodBehindField(symbol, callArgs, ctx, ancestry, range) {
|
|
10295
|
+
const keyword = ctx.pattern.resolverMethodKeyword;
|
|
10296
|
+
const written = keyword === void 0 ? void 0 : callArgs.keyword[keyword];
|
|
10297
|
+
if (written === void 0) {
|
|
10298
|
+
return bodyFromLookup(
|
|
9966
10299
|
methodInAncestry(ancestry, symbol, ctx.read),
|
|
9967
10300
|
range,
|
|
9968
10301
|
"This field",
|
|
9969
10302
|
NO_METHOD_BEHIND_IT,
|
|
9970
10303
|
ctx.bodyRead
|
|
9971
|
-
)
|
|
9972
|
-
}
|
|
10304
|
+
);
|
|
10305
|
+
}
|
|
10306
|
+
const name = stringValueOf(written, ctx.facts);
|
|
10307
|
+
if (name === null) {
|
|
10308
|
+
return methodNotSettled(
|
|
10309
|
+
`This field is resolved by the method ${written.text}, which is worked out at run time, so nothing about what it does was read here`,
|
|
10310
|
+
range
|
|
10311
|
+
);
|
|
10312
|
+
}
|
|
10313
|
+
return bodyFromLookup(
|
|
10314
|
+
methodInAncestry(ancestry, name, ctx.read),
|
|
10315
|
+
range,
|
|
10316
|
+
"This field",
|
|
10317
|
+
methodNotSettled(
|
|
10318
|
+
`This field is resolved by ${name}, which nothing in its type's ancestry defines, so nothing about what it does was read here`,
|
|
10319
|
+
range
|
|
10320
|
+
),
|
|
10321
|
+
ctx.bodyRead
|
|
10322
|
+
);
|
|
9973
10323
|
}
|
|
9974
10324
|
function literalContract(callArgs, scope, ctx) {
|
|
9975
10325
|
const typeArg = callArgs.positional[1];
|
|
@@ -9985,21 +10335,12 @@ function literalContract(callArgs, scope, ctx) {
|
|
|
9985
10335
|
async function readWiredClass(ref, scope, ctx) {
|
|
9986
10336
|
const range = rangeOf(ref);
|
|
9987
10337
|
const candidates = constantRefCandidates(ref, scope.nesting);
|
|
9988
|
-
|
|
9989
|
-
return {
|
|
9990
|
-
contract: null,
|
|
9991
|
-
body: methodNotSettled(
|
|
9992
|
-
`This field is wired to ${ref.text}, which is not a constant path this reader follows, so nothing about what it does was read here`,
|
|
9993
|
-
range
|
|
9994
|
-
)
|
|
9995
|
-
};
|
|
9996
|
-
}
|
|
9997
|
-
const reached = await reachConstant(candidates, ctx.lookup);
|
|
10338
|
+
const reached = await wiredClassInRun(ref, ctx) ?? (candidates.length === 0 ? null : await reachConstant(candidates, ctx.lookup));
|
|
9998
10339
|
if (reached === null) {
|
|
9999
10340
|
return {
|
|
10000
10341
|
contract: null,
|
|
10001
10342
|
body: methodNotSettled(
|
|
10002
|
-
`This field is wired to ${ref.text}, which this run did not read, so nothing about what it does was read here`,
|
|
10343
|
+
candidates.length === 0 ? `This field is wired to ${ref.text}, which is not a constant path this reader follows, so nothing about what it does was read here` : `This field is wired to ${ref.text}, which this run did not read, so nothing about what it does was read here`,
|
|
10003
10344
|
range
|
|
10004
10345
|
)
|
|
10005
10346
|
};
|
|
@@ -10010,20 +10351,44 @@ async function readWiredClass(ref, scope, ctx) {
|
|
|
10010
10351
|
reached.blocks,
|
|
10011
10352
|
ctx.lookup
|
|
10012
10353
|
);
|
|
10354
|
+
const body = bodyFromLookup(
|
|
10355
|
+
methodInAncestry(ancestry, ctx.pattern.resolverMethodName, ctx.read),
|
|
10356
|
+
range,
|
|
10357
|
+
`This field's ${targetQualifiedName}`,
|
|
10358
|
+
methodNotSettled(
|
|
10359
|
+
`This field is wired to ${targetQualifiedName}, which defines no ${ctx.pattern.resolverMethodName} method anywhere in its ancestry, so nothing about what it does was read here`,
|
|
10360
|
+
range
|
|
10361
|
+
),
|
|
10362
|
+
ctx.bodyRead
|
|
10363
|
+
);
|
|
10364
|
+
const hooks = ctx.pattern.resolverHooks === void 0 ? { definitions: [], unsettled: [] } : hooksRun(ancestry, ctx.pattern.resolverHooks, ctx.read);
|
|
10013
10365
|
return {
|
|
10014
10366
|
contract: readClassContract(ancestry, ctx.pattern),
|
|
10015
|
-
body:
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10367
|
+
body: {
|
|
10368
|
+
...body,
|
|
10369
|
+
readings: [
|
|
10370
|
+
...body.readings,
|
|
10371
|
+
...hooks.unsettled.map(
|
|
10372
|
+
(one) => unsettledHookReading(targetQualifiedName, one, range)
|
|
10373
|
+
)
|
|
10374
|
+
]
|
|
10375
|
+
},
|
|
10376
|
+
hooks: hooks.definitions
|
|
10025
10377
|
};
|
|
10026
10378
|
}
|
|
10379
|
+
async function wiredClassInRun(ref, ctx) {
|
|
10380
|
+
const inRun = ctx.lookup.definedInRun;
|
|
10381
|
+
if (ctx.facts === void 0 || inRun === void 0) {
|
|
10382
|
+
return null;
|
|
10383
|
+
}
|
|
10384
|
+
const classKey = classSettledOn(ctx.facts, readKey(ctx.file, ref, null));
|
|
10385
|
+
const name = classKey === void 0 ? void 0 : declaredName(ctx.facts, classKey);
|
|
10386
|
+
if (name === void 0) {
|
|
10387
|
+
return null;
|
|
10388
|
+
}
|
|
10389
|
+
const blocks = await inRun(name);
|
|
10390
|
+
return blocks === null ? null : { name, blocks };
|
|
10391
|
+
}
|
|
10027
10392
|
function readTypeCall(callArgs, scope, pattern, out) {
|
|
10028
10393
|
const typeArg = callArgs.positional[0];
|
|
10029
10394
|
if (typeArg !== void 0) {
|
|
@@ -10141,7 +10506,11 @@ function toCamelCase(name) {
|
|
|
10141
10506
|
...rest.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
10142
10507
|
].join("");
|
|
10143
10508
|
}
|
|
10144
|
-
function buildFieldUnit(pack, typeName, decl,
|
|
10509
|
+
function buildFieldUnit(pack, typeName, decl, options) {
|
|
10510
|
+
const filePath = options.filePath;
|
|
10511
|
+
const wrappers = decl.hooks.map(
|
|
10512
|
+
(hook) => hookReference(hook, displayPathIn(options, hook.block.file))
|
|
10513
|
+
);
|
|
10145
10514
|
const parameters = (decl.contract?.methodArgs ?? decl.contract?.args ?? []).map((arg, position) => ({
|
|
10146
10515
|
name: arg.name,
|
|
10147
10516
|
position,
|
|
@@ -10181,7 +10550,8 @@ function buildFieldUnit(pack, typeName, decl, filePath) {
|
|
|
10181
10550
|
dependencyCalls: [],
|
|
10182
10551
|
declaredContract: null,
|
|
10183
10552
|
...decl.body.readings.length > 0 ? { readings: decl.body.readings } : {},
|
|
10184
|
-
...graphqlDeclaredContract !== void 0 ? { graphqlDeclaredContract } : {}
|
|
10553
|
+
...graphqlDeclaredContract !== void 0 ? { graphqlDeclaredContract } : {},
|
|
10554
|
+
...wrappers.length > 0 ? { wrappers } : {}
|
|
10185
10555
|
};
|
|
10186
10556
|
}
|
|
10187
10557
|
|
|
@@ -10649,14 +11019,39 @@ function buildRubyExtractionReport(args) {
|
|
|
10649
11019
|
});
|
|
10650
11020
|
}
|
|
10651
11021
|
|
|
11022
|
+
// src/reach/closure.ts
|
|
11023
|
+
import {
|
|
11024
|
+
functionCallBinding,
|
|
11025
|
+
placeArgTargets,
|
|
11026
|
+
placeCalleeParameters,
|
|
11027
|
+
placeCalls,
|
|
11028
|
+
placePossibleCalls,
|
|
11029
|
+
ReceiverClasses,
|
|
11030
|
+
recordParameterGaps,
|
|
11031
|
+
StopList,
|
|
11032
|
+
sameDeclarations,
|
|
11033
|
+
TargetPlacements,
|
|
11034
|
+
unfollowedCallGap,
|
|
11035
|
+
worthRecording
|
|
11036
|
+
} from "@suss/behavioral-ir";
|
|
11037
|
+
import { Database, evaluate, lit as lit2, rule as rule2, variable as v2 } from "@suss/datalog";
|
|
11038
|
+
import { assembleSummary } from "@suss/extractor";
|
|
11039
|
+
import {
|
|
11040
|
+
fileOfKey as fileOfKey2,
|
|
11041
|
+
keptVariants,
|
|
11042
|
+
noDependencies,
|
|
11043
|
+
plainKeyOf,
|
|
11044
|
+
variantKey
|
|
11045
|
+
} from "@suss/resolution";
|
|
11046
|
+
|
|
10652
11047
|
// src/exits.ts
|
|
10653
11048
|
import {
|
|
10654
11049
|
markReturnsAsExitCode,
|
|
10655
|
-
SKIP_CHILDREN as
|
|
10656
|
-
walkDescendants as
|
|
11050
|
+
SKIP_CHILDREN as SKIP_CHILDREN5,
|
|
11051
|
+
walkDescendants as walkDescendants5
|
|
10657
11052
|
} from "@suss/extractor";
|
|
10658
11053
|
import { exitCodeFunctions } from "@suss/resolution";
|
|
10659
|
-
import { constantOf as
|
|
11054
|
+
import { constantOf as constantOf5 } from "@suss/values";
|
|
10660
11055
|
var EXIT_METHODS = /* @__PURE__ */ new Map([
|
|
10661
11056
|
["exit", 0],
|
|
10662
11057
|
["exit!", 1],
|
|
@@ -10665,14 +11060,14 @@ var EXIT_METHODS = /* @__PURE__ */ new Map([
|
|
|
10665
11060
|
var DEFERRED_BODY_TYPES3 = /* @__PURE__ */ new Set(["method", "singleton_method", "lambda"]);
|
|
10666
11061
|
function exitSites(root) {
|
|
10667
11062
|
const found = [];
|
|
10668
|
-
|
|
11063
|
+
walkDescendants5(root, null, children, {
|
|
10669
11064
|
at: (node) => {
|
|
10670
11065
|
const site = node.type === "call" ? exitSiteAt(node) : null;
|
|
10671
11066
|
if (site !== null) {
|
|
10672
11067
|
found.push(site);
|
|
10673
11068
|
}
|
|
10674
11069
|
},
|
|
10675
|
-
into: (node) => DEFERRED_BODY_TYPES3.has(node.type) ?
|
|
11070
|
+
into: (node) => DEFERRED_BODY_TYPES3.has(node.type) ? SKIP_CHILDREN5 : null
|
|
10676
11071
|
});
|
|
10677
11072
|
return found;
|
|
10678
11073
|
}
|
|
@@ -10709,7 +11104,7 @@ function exitCodeOf(site, facts) {
|
|
|
10709
11104
|
if (written !== void 0) {
|
|
10710
11105
|
return { type: "literal", value: written };
|
|
10711
11106
|
}
|
|
10712
|
-
const value =
|
|
11107
|
+
const value = constantOf5(evaluatedValue(code, facts));
|
|
10713
11108
|
return typeof value === "number" ? { type: "literal", value } : { type: "dynamic", sourceText: code.text };
|
|
10714
11109
|
}
|
|
10715
11110
|
function exitingBranches(method, effects, facts) {
|
|
@@ -10737,105 +11132,6 @@ function markExitCodeFunctions(summaries, exits, facts, workspaceRoot) {
|
|
|
10737
11132
|
);
|
|
10738
11133
|
}
|
|
10739
11134
|
|
|
10740
|
-
// src/facts/callbacks.ts
|
|
10741
|
-
function callbacksIn(patterns) {
|
|
10742
|
-
return patterns.flatMap(
|
|
10743
|
-
(pattern) => pattern.callbacks === void 0 ? [] : [pattern.callbacks]
|
|
10744
|
-
);
|
|
10745
|
-
}
|
|
10746
|
-
function emitClassCallbacks(db, file, classNode, declared) {
|
|
10747
|
-
const body = field(classNode, "body");
|
|
10748
|
-
if (body === null || declared.length === 0) {
|
|
10749
|
-
return;
|
|
10750
|
-
}
|
|
10751
|
-
const classKey = nodeId(file, classNode);
|
|
10752
|
-
for (const statement of runStatements(body)) {
|
|
10753
|
-
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
10754
|
-
continue;
|
|
10755
|
-
}
|
|
10756
|
-
const called = field(statement, "method")?.text ?? "";
|
|
10757
|
-
const args = readCallArgs(field(statement, "arguments"));
|
|
10758
|
-
for (const callbacks of declared) {
|
|
10759
|
-
const events = eventsOf(args, called, callbacks, db);
|
|
10760
|
-
for (const event of events) {
|
|
10761
|
-
for (const method of symbolArgumentNames(args, db)) {
|
|
10762
|
-
db.add("classCallback", [classKey, event, method]);
|
|
10763
|
-
}
|
|
10764
|
-
}
|
|
10765
|
-
}
|
|
10766
|
-
}
|
|
10767
|
-
}
|
|
10768
|
-
function eventsOf(args, called, callbacks, db) {
|
|
10769
|
-
const covered = callbacks.registeredBy[called];
|
|
10770
|
-
if (covered === void 0) {
|
|
10771
|
-
return [];
|
|
10772
|
-
}
|
|
10773
|
-
const written = args.keyword[callbacks.eventKeyword];
|
|
10774
|
-
const narrowed = written === void 0 ? null : stringValueOf(written, db);
|
|
10775
|
-
if (narrowed === null) {
|
|
10776
|
-
return covered;
|
|
10777
|
-
}
|
|
10778
|
-
return covered.filter((event) => event === narrowed);
|
|
10779
|
-
}
|
|
10780
|
-
|
|
10781
|
-
// src/facts.ts
|
|
10782
|
-
import path3 from "path";
|
|
10783
|
-
import { SKIP_CHILDREN as SKIP_CHILDREN5, walkDescendants as walkDescendants5 } from "@suss/extractor";
|
|
10784
|
-
function emitRequireFacts(db, filePath, root, known) {
|
|
10785
|
-
walkDescendants5(root, null, children, {
|
|
10786
|
-
at: (node) => {
|
|
10787
|
-
const target = requiredRelativePath(node);
|
|
10788
|
-
if (target === null) {
|
|
10789
|
-
return;
|
|
10790
|
-
}
|
|
10791
|
-
const resolved = path3.resolve(
|
|
10792
|
-
path3.dirname(filePath),
|
|
10793
|
-
target.endsWith(".rb") ? target : `${target}.rb`
|
|
10794
|
-
);
|
|
10795
|
-
if (known.has(resolved)) {
|
|
10796
|
-
db.add("importsFile", [filePath, resolved]);
|
|
10797
|
-
}
|
|
10798
|
-
},
|
|
10799
|
-
into: (node) => node.type === "argument_list" ? SKIP_CHILDREN5 : null
|
|
10800
|
-
});
|
|
10801
|
-
}
|
|
10802
|
-
function requiredRelativePath(node) {
|
|
10803
|
-
if (node.type !== "call" || field(node, "receiver") !== null || field(node, "method")?.text !== "require_relative") {
|
|
10804
|
-
return null;
|
|
10805
|
-
}
|
|
10806
|
-
const { positional } = readCallArgs(field(node, "arguments"));
|
|
10807
|
-
return positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
10808
|
-
}
|
|
10809
|
-
|
|
10810
|
-
// src/moduleSurface.ts
|
|
10811
|
-
import { moduleExportBinding } from "@suss/behavioral-ir";
|
|
10812
|
-
import {
|
|
10813
|
-
MODULE_SURFACE_RECOGNITION,
|
|
10814
|
-
moduleOfFile,
|
|
10815
|
-
settleModules
|
|
10816
|
-
} from "@suss/extractor";
|
|
10817
|
-
|
|
10818
|
-
// src/reach/closure.ts
|
|
10819
|
-
import {
|
|
10820
|
-
functionCallBinding,
|
|
10821
|
-
placeArgTargets,
|
|
10822
|
-
placeCalleeParameters,
|
|
10823
|
-
placeCalls,
|
|
10824
|
-
placePossibleCalls,
|
|
10825
|
-
ReceiverClasses,
|
|
10826
|
-
recordParameterGaps,
|
|
10827
|
-
StopList,
|
|
10828
|
-
TargetPlacements,
|
|
10829
|
-
unfollowedCallGap,
|
|
10830
|
-
worthRecording
|
|
10831
|
-
} from "@suss/behavioral-ir";
|
|
10832
|
-
import { Database, evaluate, lit as lit2, rule as rule2, variable as v2 } from "@suss/datalog";
|
|
10833
|
-
import { assembleSummary } from "@suss/extractor";
|
|
10834
|
-
import {
|
|
10835
|
-
fileOfKey as fileOfKey2,
|
|
10836
|
-
noDependencies
|
|
10837
|
-
} from "@suss/resolution";
|
|
10838
|
-
|
|
10839
11135
|
// src/flags.ts
|
|
10840
11136
|
import { SKIP_CHILDREN as SKIP_CHILDREN6, walkDescendants as walkDescendants6 } from "@suss/extractor";
|
|
10841
11137
|
var PARSE_METHODS = /* @__PURE__ */ new Set(["parse", "parse!", "permute", "permute!"]);
|
|
@@ -10941,8 +11237,14 @@ import {
|
|
|
10941
11237
|
calleeOutcomes,
|
|
10942
11238
|
classMemberName as classMemberName3,
|
|
10943
11239
|
couldBeSettled,
|
|
10944
|
-
dispatchOutcomes
|
|
11240
|
+
dispatchOutcomes,
|
|
11241
|
+
SELF_AS_CLASS_NAME as SELF_AS_CLASS_NAME2,
|
|
11242
|
+
selfFormOf,
|
|
11243
|
+
selfResultOf
|
|
10945
11244
|
} from "@suss/resolution";
|
|
11245
|
+
function selfClassOf(site) {
|
|
11246
|
+
return site.receiverClass?.name ?? site.enclosingQualifiedName;
|
|
11247
|
+
}
|
|
10946
11248
|
var STOP_FOR = {
|
|
10947
11249
|
severalSources: "multipleSources",
|
|
10948
11250
|
outsideRun: "outsideRun",
|
|
@@ -11139,6 +11441,24 @@ function asCallee(spelling, outcome, site, ctx) {
|
|
|
11139
11441
|
if (objects.length > 1) {
|
|
11140
11442
|
return stop("multipleSources");
|
|
11141
11443
|
}
|
|
11444
|
+
const fromSelf = ownReceiverForm(spelling.key, site, ctx);
|
|
11445
|
+
const selfClass = selfClassOf(site);
|
|
11446
|
+
if (fromSelf !== null && selfClass !== null) {
|
|
11447
|
+
return methodOfClass(
|
|
11448
|
+
{ ...spelling, onClassItself: fromSelf === SELF_AS_CLASS_NAME2 },
|
|
11449
|
+
selfClass,
|
|
11450
|
+
ctx
|
|
11451
|
+
);
|
|
11452
|
+
}
|
|
11453
|
+
const returned = selfResultOf(ctx.facts, spelling.key);
|
|
11454
|
+
const sentTo = returned === null ? null : classSentTo(returned.receiver, site, ctx);
|
|
11455
|
+
if (returned !== null && sentTo !== null) {
|
|
11456
|
+
return methodOfClass(
|
|
11457
|
+
{ ...spelling, onClassItself: returned.form === SELF_AS_CLASS_NAME2 },
|
|
11458
|
+
sentTo,
|
|
11459
|
+
ctx
|
|
11460
|
+
);
|
|
11461
|
+
}
|
|
11142
11462
|
const objectKey = objects[0] ?? (outcome.kind === "object" ? outcome.key : void 0);
|
|
11143
11463
|
if (objectKey !== void 0) {
|
|
11144
11464
|
return methodOnObject(spelling, objectKey, ctx);
|
|
@@ -11148,6 +11468,17 @@ function asCallee(spelling, outcome, site, ctx) {
|
|
|
11148
11468
|
}
|
|
11149
11469
|
return stop(STOP_FOR[outcome.kind] ?? "noDeclaration");
|
|
11150
11470
|
}
|
|
11471
|
+
function ownReceiverForm(key, site, ctx) {
|
|
11472
|
+
return site.method === null ? null : selfFormOf(ctx.facts, key, receiverKeyOf(site.file, site.method));
|
|
11473
|
+
}
|
|
11474
|
+
function classSentTo(receiver, site, ctx) {
|
|
11475
|
+
resolveValues(ctx.facts, [receiver]);
|
|
11476
|
+
if (ownReceiverForm(receiver, site, ctx) !== null) {
|
|
11477
|
+
return selfClassOf(site);
|
|
11478
|
+
}
|
|
11479
|
+
const [only, ...more] = objectsBehind(ctx.facts, receiver);
|
|
11480
|
+
return only === void 0 || more.length > 0 ? null : ctx.classNames.get(only) ?? null;
|
|
11481
|
+
}
|
|
11151
11482
|
function objectsBehind(facts, key) {
|
|
11152
11483
|
return answersFor2(facts, "wantedObjectOf", key);
|
|
11153
11484
|
}
|
|
@@ -11165,11 +11496,18 @@ function methodOfClass(spelling, qualifiedName, ctx) {
|
|
|
11165
11496
|
if (!spelling.onClassItself) {
|
|
11166
11497
|
return methodOnAncestryOf(qualifiedName, spelling.method, ctx);
|
|
11167
11498
|
}
|
|
11168
|
-
if (spelling.method ===
|
|
11169
|
-
return methodOnAncestryOf(qualifiedName,
|
|
11499
|
+
if (spelling.method === CONSTRUCTOR) {
|
|
11500
|
+
return methodOnAncestryOf(qualifiedName, INITIALIZE_METHOD, ctx);
|
|
11501
|
+
}
|
|
11502
|
+
return classMethodOn(qualifiedName, spelling.method, ctx);
|
|
11503
|
+
}
|
|
11504
|
+
function classMethodOn(qualifiedName, method, ctx) {
|
|
11505
|
+
const written = singletonMethodOn(qualifiedName, method, ctx);
|
|
11506
|
+
if (written.kind === "followed") {
|
|
11507
|
+
return written;
|
|
11170
11508
|
}
|
|
11171
|
-
const
|
|
11172
|
-
return
|
|
11509
|
+
const inherited = scopeOn(qualifiedName, method, ctx);
|
|
11510
|
+
return inherited === null ? written : followed(runningOn(qualifiedName, inherited, ctx));
|
|
11173
11511
|
}
|
|
11174
11512
|
function scopeOn(qualifiedName, method, ctx) {
|
|
11175
11513
|
const spelled = classMemberName3(method);
|
|
@@ -11181,7 +11519,7 @@ function scopeOn(qualifiedName, method, ctx) {
|
|
|
11181
11519
|
for (const row of ctx.facts.lookup("holdsProperty", 0, classKey)) {
|
|
11182
11520
|
const target = row[1] === spelled ? ctx.definitions.get(String(row[2])) : void 0;
|
|
11183
11521
|
if (target !== void 0) {
|
|
11184
|
-
return
|
|
11522
|
+
return target;
|
|
11185
11523
|
}
|
|
11186
11524
|
}
|
|
11187
11525
|
}
|
|
@@ -11205,7 +11543,8 @@ function leavesRoomForATopLevelMethod(reason) {
|
|
|
11205
11543
|
return reason === "noDeclaration" || reason === "outsideRun";
|
|
11206
11544
|
}
|
|
11207
11545
|
function resolveImplicitSelf(methodName, site, ctx) {
|
|
11208
|
-
const
|
|
11546
|
+
const selfClass = selfClassOf(site);
|
|
11547
|
+
const onClass = selfClass === null ? null : methodOnSelf(site, selfClass, methodName, ctx);
|
|
11209
11548
|
if (onClass !== null && (onClass.kind === "followed" || !leavesRoomForATopLevelMethod(onClass.reason))) {
|
|
11210
11549
|
return onClass;
|
|
11211
11550
|
}
|
|
@@ -11220,7 +11559,7 @@ function resolveImplicitSelf(methodName, site, ctx) {
|
|
|
11220
11559
|
return readWholeAncestry && !OBJECT_METHODS.has(methodName) && inModuleInstanceMethod(site, ctx) ? SELF_CALL_IN_MIXIN : UNSETTLED_ON_SELF;
|
|
11221
11560
|
}
|
|
11222
11561
|
function inModuleInstanceMethod(site, ctx) {
|
|
11223
|
-
const qualifiedName = site
|
|
11562
|
+
const qualifiedName = selfClassOf(site);
|
|
11224
11563
|
if (qualifiedName === null || site.method === null || inClassMethod(site, ctx)) {
|
|
11225
11564
|
return false;
|
|
11226
11565
|
}
|
|
@@ -11228,7 +11567,7 @@ function inModuleInstanceMethod(site, ctx) {
|
|
|
11228
11567
|
return own?.type === "bodies" && own.blocks.every((block) => block.info.kind === "module");
|
|
11229
11568
|
}
|
|
11230
11569
|
function declaredForTheOtherSelf(site, methodName, ctx) {
|
|
11231
|
-
const qualifiedName = site
|
|
11570
|
+
const qualifiedName = selfClassOf(site);
|
|
11232
11571
|
if (qualifiedName === null) {
|
|
11233
11572
|
return false;
|
|
11234
11573
|
}
|
|
@@ -11239,10 +11578,10 @@ function inClassMethod(site, ctx) {
|
|
|
11239
11578
|
return site.method !== null && definesClassMethod(site.method, ctx.bodyBlocks);
|
|
11240
11579
|
}
|
|
11241
11580
|
function methodOnSelf(site, qualifiedName, methodName, ctx) {
|
|
11242
|
-
if (inClassMethod(site, ctx)) {
|
|
11243
|
-
return
|
|
11581
|
+
if (!inClassMethod(site, ctx)) {
|
|
11582
|
+
return methodOnAncestryOf(qualifiedName, methodName, ctx);
|
|
11244
11583
|
}
|
|
11245
|
-
return methodOnAncestryOf(qualifiedName, methodName, ctx);
|
|
11584
|
+
return methodName === CONSTRUCTOR ? methodOnAncestryOf(qualifiedName, INITIALIZE_METHOD, ctx) : classMethodOn(qualifiedName, methodName, ctx);
|
|
11246
11585
|
}
|
|
11247
11586
|
function resolveTopLevelName(methodName, ctx) {
|
|
11248
11587
|
const candidates = ctx.topLevelMethods.get(methodName);
|
|
@@ -11265,14 +11604,37 @@ function methodOnAncestryOf(qualifiedName, methodName, ctx) {
|
|
|
11265
11604
|
dynamicNames: ctx.dynamicNames
|
|
11266
11605
|
});
|
|
11267
11606
|
if (found.type === "found") {
|
|
11268
|
-
return followed(
|
|
11607
|
+
return followed(
|
|
11608
|
+
runningOn(
|
|
11609
|
+
qualifiedName,
|
|
11610
|
+
reachedMethod(found.method, found.block, methodName),
|
|
11611
|
+
ctx
|
|
11612
|
+
)
|
|
11613
|
+
);
|
|
11269
11614
|
}
|
|
11270
11615
|
if (found.type === "unsettled") {
|
|
11271
|
-
return stop(
|
|
11616
|
+
return stop(unsettledReason2(found));
|
|
11272
11617
|
}
|
|
11273
11618
|
return NO_DECLARATION;
|
|
11274
11619
|
}
|
|
11275
|
-
function
|
|
11620
|
+
function runningOn(qualifiedName, target, ctx) {
|
|
11621
|
+
if (target.enclosingQualifiedName === qualifiedName) {
|
|
11622
|
+
return target;
|
|
11623
|
+
}
|
|
11624
|
+
const own = (ctx.ancestries.get(qualifiedName) ?? []).find(
|
|
11625
|
+
(entry) => entry.type === "bodies" && entry.name === qualifiedName
|
|
11626
|
+
);
|
|
11627
|
+
const file = own?.type === "bodies" ? own.blocks[0]?.file : void 0;
|
|
11628
|
+
if (file === void 0) {
|
|
11629
|
+
return target;
|
|
11630
|
+
}
|
|
11631
|
+
return {
|
|
11632
|
+
...target,
|
|
11633
|
+
exportPath: [qualifiedName, target.name],
|
|
11634
|
+
receiverClass: { name: qualifiedName, file }
|
|
11635
|
+
};
|
|
11636
|
+
}
|
|
11637
|
+
function unsettledReason2(found) {
|
|
11276
11638
|
if (found.cause === "dynamicDefine") {
|
|
11277
11639
|
return "definedAtLoadTime";
|
|
11278
11640
|
}
|
|
@@ -11335,11 +11697,12 @@ async function reachedFunctions(seeds, options) {
|
|
|
11335
11697
|
const charges = /* @__PURE__ */ new Map();
|
|
11336
11698
|
const beforeGaps = /* @__PURE__ */ new Map();
|
|
11337
11699
|
const functionFor = (target) => {
|
|
11338
|
-
const node = replay?.nodeOf(target.key) ?? null;
|
|
11339
|
-
|
|
11700
|
+
const node = replay?.nodeOf(plainKeyOf(target.key)) ?? null;
|
|
11701
|
+
const found = node === null ? null : { ...target, node };
|
|
11702
|
+
if (found === null || keyOf(found) !== target.key) {
|
|
11340
11703
|
throw new ReplayFailed(target.key);
|
|
11341
11704
|
}
|
|
11342
|
-
return
|
|
11705
|
+
return found;
|
|
11343
11706
|
};
|
|
11344
11707
|
for (const seed of seeds) {
|
|
11345
11708
|
seedKeys.add(seed.key);
|
|
@@ -11399,7 +11762,10 @@ async function reachedFunctions(seeds, options) {
|
|
|
11399
11762
|
if (scan.followed.length > 0) {
|
|
11400
11763
|
followedKeys.add(key);
|
|
11401
11764
|
}
|
|
11402
|
-
|
|
11765
|
+
const calledHere = scan.followed.flatMap(
|
|
11766
|
+
(target) => target.receiverClass === void 0 ? [target] : [plainOf(target), target]
|
|
11767
|
+
);
|
|
11768
|
+
for (const target of calledHere) {
|
|
11403
11769
|
const calleeKey = keyOf(target);
|
|
11404
11770
|
if (!functionByKey.has(calleeKey)) {
|
|
11405
11771
|
functionByKey.set(calleeKey, target);
|
|
@@ -11420,12 +11786,22 @@ async function reachedFunctions(seeds, options) {
|
|
|
11420
11786
|
options.storage
|
|
11421
11787
|
);
|
|
11422
11788
|
}
|
|
11789
|
+
const placementsOf = (key) => ({
|
|
11790
|
+
targets: targetsByKey.get(key),
|
|
11791
|
+
argTargets: argTargetsByKey.get(key),
|
|
11792
|
+
possibleTargets: possibleTargetsByKey.get(key)
|
|
11793
|
+
});
|
|
11794
|
+
const onSubclass = keptVariants(
|
|
11795
|
+
[...functionByKey].flatMap(
|
|
11796
|
+
([key, target]) => target.receiverClass === void 0 ? [] : [variantOf(key, scans, placementsOf)]
|
|
11797
|
+
)
|
|
11798
|
+
);
|
|
11423
11799
|
const summaries = [];
|
|
11424
11800
|
const summariesByKey = /* @__PURE__ */ new Map();
|
|
11425
11801
|
for (const [keyAtom] of db.facts("reachable")) {
|
|
11426
11802
|
const key = String(keyAtom);
|
|
11427
11803
|
const target = functionByKey.get(key);
|
|
11428
|
-
if (seedKeys.has(key) || target === void 0) {
|
|
11804
|
+
if (seedKeys.has(key) || target === void 0 || target.receiverClass !== void 0 && !onSubclass.has(key)) {
|
|
11429
11805
|
continue;
|
|
11430
11806
|
}
|
|
11431
11807
|
const kept = replay?.summaryOf(key);
|
|
@@ -11440,6 +11816,12 @@ async function reachedFunctions(seeds, options) {
|
|
|
11440
11816
|
const raw = libraryUnit(target, options);
|
|
11441
11817
|
dropPropertyReads(raw, propertyReadsByKey.get(key));
|
|
11442
11818
|
const summary2 = assembleSummary(raw, { gapHandling });
|
|
11819
|
+
if (target.receiverClass !== void 0) {
|
|
11820
|
+
summary2.receiverClass = subclassAt(
|
|
11821
|
+
target.receiverClass,
|
|
11822
|
+
options.displayPathOf
|
|
11823
|
+
);
|
|
11824
|
+
}
|
|
11443
11825
|
if (gapHandling !== "silent") {
|
|
11444
11826
|
summary2.gaps.push(
|
|
11445
11827
|
...(stopsByKey.get(key) ?? []).map(unfollowedCallGap)
|
|
@@ -11481,6 +11863,16 @@ async function reachedFunctions(seeds, options) {
|
|
|
11481
11863
|
beforeGaps
|
|
11482
11864
|
};
|
|
11483
11865
|
}
|
|
11866
|
+
function variantOf(key, scans, placementsOf) {
|
|
11867
|
+
return {
|
|
11868
|
+
key,
|
|
11869
|
+
differs: !sameDeclarations(
|
|
11870
|
+
placementsOf(key),
|
|
11871
|
+
placementsOf(plainKeyOf(key))
|
|
11872
|
+
),
|
|
11873
|
+
calls: scans.get(key)?.followed.map((target) => target.key) ?? []
|
|
11874
|
+
};
|
|
11875
|
+
}
|
|
11484
11876
|
var ReplayFailed = class extends Error {
|
|
11485
11877
|
constructor(key) {
|
|
11486
11878
|
super(`The cache stored a method at ${key}, and nothing is there now.`);
|
|
@@ -11571,7 +11963,8 @@ function storedScan(scan) {
|
|
|
11571
11963
|
file: target.file,
|
|
11572
11964
|
name: target.name,
|
|
11573
11965
|
exportPath: target.exportPath,
|
|
11574
|
-
enclosingQualifiedName: target.enclosingQualifiedName
|
|
11966
|
+
enclosingQualifiedName: target.enclosingQualifiedName,
|
|
11967
|
+
...target.receiverClass === void 0 ? {} : { receiverClass: target.receiverClass }
|
|
11575
11968
|
})),
|
|
11576
11969
|
stops: scan.stops,
|
|
11577
11970
|
targets: [...scan.targets],
|
|
@@ -11608,7 +12001,25 @@ function scanFromStored(stored, functionFor) {
|
|
|
11608
12001
|
};
|
|
11609
12002
|
}
|
|
11610
12003
|
function keyOf(target) {
|
|
11611
|
-
|
|
12004
|
+
const key = nodeId(target.file, target.node);
|
|
12005
|
+
return target.receiverClass === void 0 ? key : variantKey(key, target.receiverClass);
|
|
12006
|
+
}
|
|
12007
|
+
function plainOf(target) {
|
|
12008
|
+
const { receiverClass: _, ...plain } = target;
|
|
12009
|
+
return plain.enclosingQualifiedName === null ? plain : { ...plain, exportPath: [plain.enclosingQualifiedName, plain.name] };
|
|
12010
|
+
}
|
|
12011
|
+
function placeOf(target, displayPathOf) {
|
|
12012
|
+
const place = {
|
|
12013
|
+
file: displayPathOf(target.file),
|
|
12014
|
+
span: spanOf(target.node)
|
|
12015
|
+
};
|
|
12016
|
+
return target.receiverClass === void 0 ? place : {
|
|
12017
|
+
...place,
|
|
12018
|
+
receiverClass: subclassAt(target.receiverClass, displayPathOf)
|
|
12019
|
+
};
|
|
12020
|
+
}
|
|
12021
|
+
function subclassAt(subclass, displayPathOf) {
|
|
12022
|
+
return { name: subclass.name, file: displayPathOf(subclass.file) };
|
|
11612
12023
|
}
|
|
11613
12024
|
function dropPropertyReads(raw, callees) {
|
|
11614
12025
|
if (callees === void 0) {
|
|
@@ -11681,8 +12092,9 @@ function siteOf2(source) {
|
|
|
11681
12092
|
return {
|
|
11682
12093
|
file: source.file,
|
|
11683
12094
|
method: localsOwnerOf(source.node),
|
|
11684
|
-
owner:
|
|
11685
|
-
enclosingQualifiedName: source.enclosingQualifiedName
|
|
12095
|
+
owner: nodeId(source.file, source.node),
|
|
12096
|
+
enclosingQualifiedName: source.enclosingQualifiedName,
|
|
12097
|
+
receiverClass: source.receiverClass
|
|
11686
12098
|
};
|
|
11687
12099
|
}
|
|
11688
12100
|
function localsOwnerOf(node) {
|
|
@@ -11725,17 +12137,21 @@ function scanBody(source, ctx, options, read) {
|
|
|
11725
12137
|
const seen = /* @__PURE__ */ new Set();
|
|
11726
12138
|
const parameterCallsSeen = /* @__PURE__ */ new Set();
|
|
11727
12139
|
const keptArgless = /* @__PURE__ */ new Set();
|
|
11728
|
-
const recordPassedArgs = (call, callee,
|
|
12140
|
+
const recordPassedArgs = (call, callee, target) => {
|
|
11729
12141
|
const args = field(call, "arguments");
|
|
11730
12142
|
if (args === null) {
|
|
11731
12143
|
return;
|
|
11732
12144
|
}
|
|
12145
|
+
const calleeKeys = target === null ? [] : [keyOf(target), keyOf(plainOf(target))];
|
|
12146
|
+
const passes = (position) => {
|
|
12147
|
+
for (const calleeKey of calleeKeys) {
|
|
12148
|
+
passedPositions.add(`${calleeKey}#${position}`);
|
|
12149
|
+
}
|
|
12150
|
+
};
|
|
11733
12151
|
const argNodes = bodyStatements(args);
|
|
11734
12152
|
for (let position = 0; position < argNodes.length; position += 1) {
|
|
11735
12153
|
if (argNodes[position]?.type === LAMBDA_TYPE) {
|
|
11736
|
-
|
|
11737
|
-
passedPositions.add(`${calleeKey}#${position}`);
|
|
11738
|
-
}
|
|
12154
|
+
passes(position);
|
|
11739
12155
|
continue;
|
|
11740
12156
|
}
|
|
11741
12157
|
const symbol = methodReferenceSymbol(argNodes[position]);
|
|
@@ -11751,13 +12167,8 @@ function scanBody(source, ctx, options, read) {
|
|
|
11751
12167
|
seen.add(key);
|
|
11752
12168
|
followed2.push(resolved);
|
|
11753
12169
|
}
|
|
11754
|
-
placements.placeArg(callee, position,
|
|
11755
|
-
|
|
11756
|
-
span: spanOf(resolved.node)
|
|
11757
|
-
});
|
|
11758
|
-
if (calleeKey !== null) {
|
|
11759
|
-
passedPositions.add(`${calleeKey}#${position}`);
|
|
11760
|
-
}
|
|
12170
|
+
placements.placeArg(callee, position, placeOf(resolved, displayPathOf));
|
|
12171
|
+
passes(position);
|
|
11761
12172
|
}
|
|
11762
12173
|
};
|
|
11763
12174
|
const followCallback = (name, key) => {
|
|
@@ -11793,10 +12204,7 @@ function scanBody(source, ctx, options, read) {
|
|
|
11793
12204
|
dispatched.targets.forEach(follow);
|
|
11794
12205
|
placements.placePossible(
|
|
11795
12206
|
callee,
|
|
11796
|
-
dispatched.targets.map((target) => (
|
|
11797
|
-
file: displayPathOf(target.file),
|
|
11798
|
-
span: spanOf(target.node)
|
|
11799
|
-
}))
|
|
12207
|
+
dispatched.targets.map((target) => placeOf(target, displayPathOf))
|
|
11800
12208
|
);
|
|
11801
12209
|
if (dispatched.unresolved > 0) {
|
|
11802
12210
|
stops.add({ callee, reason: "unsettledValue" });
|
|
@@ -11828,7 +12236,7 @@ function scanBody(source, ctx, options, read) {
|
|
|
11828
12236
|
recordPassedArgs(
|
|
11829
12237
|
call,
|
|
11830
12238
|
callee,
|
|
11831
|
-
outcome.kind === "followed" ?
|
|
12239
|
+
outcome.kind === "followed" ? outcome.target : null
|
|
11832
12240
|
);
|
|
11833
12241
|
if (outcome.kind === "stopped") {
|
|
11834
12242
|
const claimed = options.storage !== void 0 && storageClaims(call, callSite.file, options.storage, callSite.method);
|
|
@@ -11869,10 +12277,7 @@ function scanBody(source, ctx, options, read) {
|
|
|
11869
12277
|
function placeCallee(placements, outcome, call, where) {
|
|
11870
12278
|
const callee = calleeText(call);
|
|
11871
12279
|
if (outcome.kind === "followed") {
|
|
11872
|
-
placements.place(callee,
|
|
11873
|
-
file: where.displayPathOf(outcome.target.file),
|
|
11874
|
-
span: spanOf(outcome.target.node)
|
|
11875
|
-
});
|
|
12280
|
+
placements.place(callee, placeOf(outcome.target, where.displayPathOf));
|
|
11876
12281
|
return;
|
|
11877
12282
|
}
|
|
11878
12283
|
if (outcome.matchByName === true) {
|
|
@@ -11926,7 +12331,7 @@ function libraryUnit(target, options, binding = functionCallBinding({
|
|
|
11926
12331
|
emitEvent: null,
|
|
11927
12332
|
location: range
|
|
11928
12333
|
},
|
|
11929
|
-
effects,
|
|
12334
|
+
effects: [],
|
|
11930
12335
|
location: range,
|
|
11931
12336
|
isDefault: true
|
|
11932
12337
|
}
|
|
@@ -11946,7 +12351,10 @@ function libraryUnit(target, options, binding = functionCallBinding({
|
|
|
11946
12351
|
parameters,
|
|
11947
12352
|
branches: withSlotSources(
|
|
11948
12353
|
branches.map(
|
|
11949
|
-
(branch) =>
|
|
12354
|
+
(branch) => withEffectsThatRun(branch, {
|
|
12355
|
+
effects,
|
|
12356
|
+
extraEffects: body.extraEffects
|
|
12357
|
+
})
|
|
11950
12358
|
),
|
|
11951
12359
|
body.provenance
|
|
11952
12360
|
),
|
|
@@ -11981,7 +12389,204 @@ function parameterName(param) {
|
|
|
11981
12389
|
return named2 !== null && named2.type === "identifier" ? named2.text : null;
|
|
11982
12390
|
}
|
|
11983
12391
|
|
|
12392
|
+
// src/entryPoints.ts
|
|
12393
|
+
function declaredEntryUnits(root, file, options) {
|
|
12394
|
+
const units = [];
|
|
12395
|
+
for (const pack of options.packs) {
|
|
12396
|
+
for (const entry of pack.entryPoints?.(file) ?? []) {
|
|
12397
|
+
const method = entryMethod(root, entry.name, options);
|
|
12398
|
+
if (method === null) {
|
|
12399
|
+
continue;
|
|
12400
|
+
}
|
|
12401
|
+
for (const declared of entry.units) {
|
|
12402
|
+
const raw = libraryUnit(
|
|
12403
|
+
{
|
|
12404
|
+
file,
|
|
12405
|
+
node: method.node,
|
|
12406
|
+
name: declared.name,
|
|
12407
|
+
exportPath: method.exportPath
|
|
12408
|
+
},
|
|
12409
|
+
options,
|
|
12410
|
+
declared.binding
|
|
12411
|
+
);
|
|
12412
|
+
const unit = {
|
|
12413
|
+
...raw,
|
|
12414
|
+
identity: { ...raw.identity, kind: declared.kind },
|
|
12415
|
+
deployableUnit: declared.deployableUnit,
|
|
12416
|
+
...declared.metadata !== void 0 ? { packMetadata: declared.metadata } : {}
|
|
12417
|
+
};
|
|
12418
|
+
units.push(unit);
|
|
12419
|
+
options.onReachSeed(unit, {
|
|
12420
|
+
file,
|
|
12421
|
+
node: method.node,
|
|
12422
|
+
enclosingQualifiedName: method.enclosingQualifiedName
|
|
12423
|
+
});
|
|
12424
|
+
}
|
|
12425
|
+
}
|
|
12426
|
+
}
|
|
12427
|
+
return units;
|
|
12428
|
+
}
|
|
12429
|
+
function entryMethod(root, name, options) {
|
|
12430
|
+
const dot = name.lastIndexOf(".");
|
|
12431
|
+
if (dot === -1) {
|
|
12432
|
+
const node = instanceMethodsByName(root, options.bodyBlocks).get(name);
|
|
12433
|
+
return node === void 0 ? null : { node, exportPath: [name], enclosingQualifiedName: null };
|
|
12434
|
+
}
|
|
12435
|
+
const className = name.slice(0, dot);
|
|
12436
|
+
const methodName = name.slice(dot + 1);
|
|
12437
|
+
let found = null;
|
|
12438
|
+
walkDefinitions(root, (info) => {
|
|
12439
|
+
if (found !== null || info.qualifiedName !== className) {
|
|
12440
|
+
return;
|
|
12441
|
+
}
|
|
12442
|
+
const node = info.bodyNode === null ? void 0 : singletonMethodsByName(info.bodyNode, options.bodyBlocks).get(
|
|
12443
|
+
methodName
|
|
12444
|
+
);
|
|
12445
|
+
if (node !== void 0) {
|
|
12446
|
+
found = {
|
|
12447
|
+
node,
|
|
12448
|
+
exportPath: [className, methodName],
|
|
12449
|
+
enclosingQualifiedName: className
|
|
12450
|
+
};
|
|
12451
|
+
}
|
|
12452
|
+
});
|
|
12453
|
+
return found;
|
|
12454
|
+
}
|
|
12455
|
+
|
|
12456
|
+
// src/erbTemplate.ts
|
|
12457
|
+
var OPEN = "<%";
|
|
12458
|
+
var CLOSE = "%>";
|
|
12459
|
+
var LITERAL_OPEN = "<%%";
|
|
12460
|
+
var COMMENT_MARK = "#";
|
|
12461
|
+
var OUTPUT_MARK = "=";
|
|
12462
|
+
var TRIM_OPEN_MARK = "-";
|
|
12463
|
+
var TRIM_CLOSE_MARK = "-";
|
|
12464
|
+
var CODE_CLOSE = "; ";
|
|
12465
|
+
var OUTPUT_CLOSE = "];";
|
|
12466
|
+
var OUTPUT_OPEN = "[";
|
|
12467
|
+
function rubyInErbTemplate(template) {
|
|
12468
|
+
const parts = [];
|
|
12469
|
+
let at = 0;
|
|
12470
|
+
while (at < template.length) {
|
|
12471
|
+
const open = template.indexOf(OPEN, at);
|
|
12472
|
+
if (open === -1) {
|
|
12473
|
+
parts.push(blanked(template.slice(at)));
|
|
12474
|
+
break;
|
|
12475
|
+
}
|
|
12476
|
+
parts.push(blanked(template.slice(at, open)));
|
|
12477
|
+
if (template.startsWith(LITERAL_OPEN, open)) {
|
|
12478
|
+
parts.push(blanked(LITERAL_OPEN));
|
|
12479
|
+
at = open + LITERAL_OPEN.length;
|
|
12480
|
+
continue;
|
|
12481
|
+
}
|
|
12482
|
+
const close = template.indexOf(CLOSE, open + OPEN.length);
|
|
12483
|
+
if (close === -1) {
|
|
12484
|
+
parts.push(blanked(template.slice(open)));
|
|
12485
|
+
break;
|
|
12486
|
+
}
|
|
12487
|
+
parts.push(tagAsRuby(template.slice(open, close + CLOSE.length)));
|
|
12488
|
+
at = close + CLOSE.length;
|
|
12489
|
+
}
|
|
12490
|
+
return parts.join("");
|
|
12491
|
+
}
|
|
12492
|
+
function tagAsRuby(tag) {
|
|
12493
|
+
const mark = tag.charAt(OPEN.length);
|
|
12494
|
+
if (mark === COMMENT_MARK) {
|
|
12495
|
+
return blanked(tag);
|
|
12496
|
+
}
|
|
12497
|
+
const prints = mark === OUTPUT_MARK;
|
|
12498
|
+
const codeStart = OPEN.length + (prints || mark === TRIM_OPEN_MARK ? mark.length : 0);
|
|
12499
|
+
const beforeClose = tag.length - CLOSE.length;
|
|
12500
|
+
const codeEnd = beforeClose > codeStart && tag.charAt(beforeClose - 1) === TRIM_CLOSE_MARK ? beforeClose - 1 : beforeClose;
|
|
12501
|
+
const opening = blanked(tag.slice(0, codeStart));
|
|
12502
|
+
return [
|
|
12503
|
+
prints ? `${OUTPUT_OPEN}${opening.slice(OUTPUT_OPEN.length)}` : opening,
|
|
12504
|
+
tag.slice(codeStart, codeEnd),
|
|
12505
|
+
blanked(tag.slice(codeEnd, beforeClose)),
|
|
12506
|
+
prints ? OUTPUT_CLOSE : CODE_CLOSE
|
|
12507
|
+
].join("");
|
|
12508
|
+
}
|
|
12509
|
+
function blanked(text3) {
|
|
12510
|
+
return text3.replace(/[^\r\n]/g, " ");
|
|
12511
|
+
}
|
|
12512
|
+
|
|
12513
|
+
// src/facts/callbacks.ts
|
|
12514
|
+
function callbacksIn(patterns) {
|
|
12515
|
+
return patterns.flatMap(
|
|
12516
|
+
(pattern) => pattern.callbacks === void 0 ? [] : [pattern.callbacks]
|
|
12517
|
+
);
|
|
12518
|
+
}
|
|
12519
|
+
function emitClassCallbacks(db, file, classNode, declared) {
|
|
12520
|
+
const body = field(classNode, "body");
|
|
12521
|
+
if (body === null || declared.length === 0) {
|
|
12522
|
+
return;
|
|
12523
|
+
}
|
|
12524
|
+
const classKey = nodeId(file, classNode);
|
|
12525
|
+
for (const statement of runStatements(body)) {
|
|
12526
|
+
if (statement.type !== "call" || field(statement, "receiver") !== null) {
|
|
12527
|
+
continue;
|
|
12528
|
+
}
|
|
12529
|
+
const called = field(statement, "method")?.text ?? "";
|
|
12530
|
+
const args = readCallArgs(field(statement, "arguments"));
|
|
12531
|
+
for (const callbacks of declared) {
|
|
12532
|
+
const events = eventsOf(args, called, callbacks, db);
|
|
12533
|
+
for (const event of events) {
|
|
12534
|
+
for (const method of symbolArgumentNames(args, db)) {
|
|
12535
|
+
db.add("classCallback", [classKey, event, method]);
|
|
12536
|
+
}
|
|
12537
|
+
}
|
|
12538
|
+
}
|
|
12539
|
+
}
|
|
12540
|
+
}
|
|
12541
|
+
function eventsOf(args, called, callbacks, db) {
|
|
12542
|
+
const covered = callbacks.registeredBy[called];
|
|
12543
|
+
if (covered === void 0) {
|
|
12544
|
+
return [];
|
|
12545
|
+
}
|
|
12546
|
+
const written = args.keyword[callbacks.eventKeyword];
|
|
12547
|
+
const narrowed = written === void 0 ? null : stringValueOf(written, db);
|
|
12548
|
+
if (narrowed === null) {
|
|
12549
|
+
return covered;
|
|
12550
|
+
}
|
|
12551
|
+
return covered.filter((event) => event === narrowed);
|
|
12552
|
+
}
|
|
12553
|
+
|
|
12554
|
+
// src/facts.ts
|
|
12555
|
+
import path3 from "path";
|
|
12556
|
+
import { SKIP_CHILDREN as SKIP_CHILDREN7, walkDescendants as walkDescendants7 } from "@suss/extractor";
|
|
12557
|
+
function emitRequireFacts(db, filePath, root, known) {
|
|
12558
|
+
walkDescendants7(root, null, children, {
|
|
12559
|
+
at: (node) => {
|
|
12560
|
+
const target = requiredRelativePath(node);
|
|
12561
|
+
if (target === null) {
|
|
12562
|
+
return;
|
|
12563
|
+
}
|
|
12564
|
+
const resolved = path3.resolve(
|
|
12565
|
+
path3.dirname(filePath),
|
|
12566
|
+
target.endsWith(".rb") ? target : `${target}.rb`
|
|
12567
|
+
);
|
|
12568
|
+
if (known.has(resolved)) {
|
|
12569
|
+
db.add("importsFile", [filePath, resolved]);
|
|
12570
|
+
}
|
|
12571
|
+
},
|
|
12572
|
+
into: (node) => node.type === "argument_list" ? SKIP_CHILDREN7 : null
|
|
12573
|
+
});
|
|
12574
|
+
}
|
|
12575
|
+
function requiredRelativePath(node) {
|
|
12576
|
+
if (node.type !== "call" || field(node, "receiver") !== null || field(node, "method")?.text !== "require_relative") {
|
|
12577
|
+
return null;
|
|
12578
|
+
}
|
|
12579
|
+
const { positional } = readCallArgs(field(node, "arguments"));
|
|
12580
|
+
return positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
12581
|
+
}
|
|
12582
|
+
|
|
11984
12583
|
// src/moduleSurface.ts
|
|
12584
|
+
import { moduleExportBinding } from "@suss/behavioral-ir";
|
|
12585
|
+
import {
|
|
12586
|
+
MODULE_SURFACE_RECOGNITION,
|
|
12587
|
+
moduleOfFile,
|
|
12588
|
+
settleModules
|
|
12589
|
+
} from "@suss/extractor";
|
|
11985
12590
|
function settleRubyModules(modules) {
|
|
11986
12591
|
return settleModules(modules, ["{name}.rb", "../{name}.rb"]);
|
|
11987
12592
|
}
|
|
@@ -12584,12 +13189,12 @@ var SharedGroupIndex = class {
|
|
|
12584
13189
|
};
|
|
12585
13190
|
function emitSharedGroupBinds(db, index, files) {
|
|
12586
13191
|
for (const at of files) {
|
|
12587
|
-
for (const site of
|
|
13192
|
+
for (const site of sitesIn2(index, at)) {
|
|
12588
13193
|
emitSiteBinds(db, index, site, at.pattern);
|
|
12589
13194
|
}
|
|
12590
13195
|
}
|
|
12591
13196
|
}
|
|
12592
|
-
function
|
|
13197
|
+
function sitesIn2(index, at) {
|
|
12593
13198
|
const found = [];
|
|
12594
13199
|
const visit = (node) => {
|
|
12595
13200
|
for (const child of children(node)) {
|
|
@@ -12830,7 +13435,25 @@ function replaysCleanly(units, discovered) {
|
|
|
12830
13435
|
}
|
|
12831
13436
|
return true;
|
|
12832
13437
|
}
|
|
13438
|
+
function walkedFilesOf(options) {
|
|
13439
|
+
const given = new Set(options.files);
|
|
13440
|
+
const templates = [
|
|
13441
|
+
...new Set(options.packs.flatMap((pack) => pack.bootTemplates?.() ?? []))
|
|
13442
|
+
].filter((file) => !given.has(file)).sort();
|
|
13443
|
+
return {
|
|
13444
|
+
files: [...options.files, ...templates],
|
|
13445
|
+
templates: new Set(templates)
|
|
13446
|
+
};
|
|
13447
|
+
}
|
|
13448
|
+
function rubySourceOf(absPath, templates) {
|
|
13449
|
+
if (!fs3.existsSync(absPath)) {
|
|
13450
|
+
return null;
|
|
13451
|
+
}
|
|
13452
|
+
const text3 = fs3.readFileSync(absPath, "utf8");
|
|
13453
|
+
return templates.has(absPath) ? rubyInErbTemplate(text3) : text3;
|
|
13454
|
+
}
|
|
12833
13455
|
async function extractRubyProject(options) {
|
|
13456
|
+
const walked = walkedFilesOf(options);
|
|
12834
13457
|
const timer = options.onTiming !== void 0 ? createTimer() : noopTimer();
|
|
12835
13458
|
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
12836
13459
|
options.cacheDir === null ? null : options.cacheDir ?? (options.projectRoot !== void 0 ? extractionCacheDirFor(options.projectRoot) : null)
|
|
@@ -12847,8 +13470,8 @@ async function extractRubyProject(options) {
|
|
|
12847
13470
|
modules: settleRubyModules(options.modules)
|
|
12848
13471
|
})}`;
|
|
12849
13472
|
const input = {
|
|
12850
|
-
files: cacheDir === null ? [] :
|
|
12851
|
-
adapterPacksDigest: cacheDir === null ? packsDigest : runDigest(packsDigest, options.packs,
|
|
13473
|
+
files: cacheDir === null ? [] : walked.files,
|
|
13474
|
+
adapterPacksDigest: cacheDir === null ? packsDigest : runDigest(packsDigest, options.packs, walked.files)
|
|
12852
13475
|
};
|
|
12853
13476
|
const lookup = await timer.timeAsync(
|
|
12854
13477
|
"cache.lookup",
|
|
@@ -12876,7 +13499,7 @@ async function extractRubyProject(options) {
|
|
|
12876
13499
|
const records = plan === null ? null : recordsToReplay(plan.roots, plan.changed, plan.removed);
|
|
12877
13500
|
if (plan !== null && records !== null) {
|
|
12878
13501
|
try {
|
|
12879
|
-
return await runRuby(options, cache, { plan, records });
|
|
13502
|
+
return await runRuby(options, walked, cache, { plan, records });
|
|
12880
13503
|
} catch (error) {
|
|
12881
13504
|
if (!(error instanceof ReplayFailed)) {
|
|
12882
13505
|
throw error;
|
|
@@ -12884,7 +13507,7 @@ async function extractRubyProject(options) {
|
|
|
12884
13507
|
}
|
|
12885
13508
|
}
|
|
12886
13509
|
options.onCacheDiagnostic?.(lookup.diagnostic);
|
|
12887
|
-
return runRuby(options, cache, null);
|
|
13510
|
+
return runRuby(options, walked, cache, null);
|
|
12888
13511
|
}
|
|
12889
13512
|
function seedFromStored(seed, nodeOf) {
|
|
12890
13513
|
const node = nodeOf(seed.key);
|
|
@@ -12901,13 +13524,13 @@ function seedFromStored(seed, nodeOf) {
|
|
|
12901
13524
|
function addForSeed(bySeed, seedKey, summary) {
|
|
12902
13525
|
bySeed.set(seedKey, [...bySeed.get(seedKey) ?? [], summary]);
|
|
12903
13526
|
}
|
|
12904
|
-
async function runRuby(options, cache, previous) {
|
|
13527
|
+
async function runRuby(options, walked, cache, previous) {
|
|
12905
13528
|
const { timer, cacheDir } = cache;
|
|
12906
13529
|
const db = new Database2();
|
|
12907
13530
|
const summaries = [];
|
|
12908
13531
|
const gapHandling = options.gapHandling ?? "permissive";
|
|
12909
13532
|
const tallies = createPackTallies(options.packs);
|
|
12910
|
-
const ledger = cacheDir === null ? null : new DependencyLedger(
|
|
13533
|
+
const ledger = cacheDir === null ? null : new DependencyLedger(walked.files);
|
|
12911
13534
|
const log = ledger === null ? null : new FactLog(db, ledger);
|
|
12912
13535
|
if (ledger !== null) {
|
|
12913
13536
|
observeDemand(db, ledger);
|
|
@@ -12915,12 +13538,12 @@ async function runRuby(options, cache, previous) {
|
|
|
12915
13538
|
const parses = options.keptParses ?? keptRubyParses();
|
|
12916
13539
|
const trees = createFileCache(
|
|
12917
13540
|
(source, absPath) => parses.parse(absPath, source, parseRubyRoot),
|
|
12918
|
-
(absPath) =>
|
|
13541
|
+
(absPath) => rubySourceOf(absPath, walked.templates)
|
|
12919
13542
|
);
|
|
12920
13543
|
const fileCache = ledger === null ? trees : watchFileCache(trees, ledger);
|
|
12921
13544
|
const facts = new RunFacts(db, options.packs);
|
|
12922
13545
|
const { parsed, bodyBlocks, scopeCalls } = facts;
|
|
12923
|
-
for (const file of
|
|
13546
|
+
for (const file of walked.files) {
|
|
12924
13547
|
await timer.timeAsync("parse", async () => {
|
|
12925
13548
|
const root = await trees.get(file);
|
|
12926
13549
|
if (root !== null) {
|
|
@@ -12930,7 +13553,7 @@ async function runRuby(options, cache, previous) {
|
|
|
12930
13553
|
}
|
|
12931
13554
|
});
|
|
12932
13555
|
}
|
|
12933
|
-
options.keptParses?.keepOnly(
|
|
13556
|
+
options.keptParses?.keepOnly(walked.files);
|
|
12934
13557
|
const rootsByFile = new Map(parsed.map(({ file, root }) => [file, root]));
|
|
12935
13558
|
const dynamicNames = timer.time("discover", () => {
|
|
12936
13559
|
log?.startJoined();
|
|
@@ -13056,6 +13679,16 @@ async function runRuby(options, cache, previous) {
|
|
|
13056
13679
|
displayPathOf,
|
|
13057
13680
|
facts: db,
|
|
13058
13681
|
onReachSeed
|
|
13682
|
+
}),
|
|
13683
|
+
...declaredEntryUnits(root, file, {
|
|
13684
|
+
packs: options.packs,
|
|
13685
|
+
...storage === void 0 ? {} : { storage },
|
|
13686
|
+
inheritedMethods,
|
|
13687
|
+
bodyBlocks,
|
|
13688
|
+
dynamicNames: watchedNames,
|
|
13689
|
+
displayPathOf,
|
|
13690
|
+
facts: db,
|
|
13691
|
+
onReachSeed
|
|
13059
13692
|
})
|
|
13060
13693
|
];
|
|
13061
13694
|
const rawUnits = await timer.timeAsync(
|
|
@@ -13104,7 +13737,7 @@ async function runRuby(options, cache, previous) {
|
|
|
13104
13737
|
seedKeys.add(moduleKey);
|
|
13105
13738
|
seeds.push(moduleSeed);
|
|
13106
13739
|
const readLoadTime = () => ({
|
|
13107
|
-
reads: envReadEffects(root, { db, file }),
|
|
13740
|
+
reads: envReadEffects(root, { db, file }).map((one) => one.effect),
|
|
13108
13741
|
calls: moduleScopeInvocationEffects(
|
|
13109
13742
|
root,
|
|
13110
13743
|
inheritedMethods,
|
|
@@ -13336,7 +13969,7 @@ async function runRuby(options, cache, previous) {
|
|
|
13336
13969
|
buildRubyExtractionReport({
|
|
13337
13970
|
packs: options.packs,
|
|
13338
13971
|
tallies,
|
|
13339
|
-
filesWalked:
|
|
13972
|
+
filesWalked: walked.files.length,
|
|
13340
13973
|
summaries: composed
|
|
13341
13974
|
})
|
|
13342
13975
|
);
|
|
@@ -13562,7 +14195,7 @@ var RubyWhySession = class {
|
|
|
13562
14195
|
// src/stubEvidence.ts
|
|
13563
14196
|
import fs5 from "fs";
|
|
13564
14197
|
import path6 from "path";
|
|
13565
|
-
import { SKIP_CHILDREN as
|
|
14198
|
+
import { SKIP_CHILDREN as SKIP_CHILDREN8, walkDescendants as walkDescendants8 } from "@suss/extractor";
|
|
13566
14199
|
function bareLetters(name) {
|
|
13567
14200
|
return name.replaceAll(/[-_]/g, "").toLowerCase();
|
|
13568
14201
|
}
|
|
@@ -13586,14 +14219,14 @@ function requireTarget(node) {
|
|
|
13586
14219
|
}
|
|
13587
14220
|
function requireSitesIn(root, file) {
|
|
13588
14221
|
const found = [];
|
|
13589
|
-
|
|
14222
|
+
walkDescendants8(root, null, children, {
|
|
13590
14223
|
at: (node) => {
|
|
13591
14224
|
const target = requireTarget(node);
|
|
13592
14225
|
if (target !== null) {
|
|
13593
14226
|
found.push({ target, file, line: rangeOf(node).start });
|
|
13594
14227
|
}
|
|
13595
14228
|
},
|
|
13596
|
-
into: (node) => node.type === "argument_list" ?
|
|
14229
|
+
into: (node) => node.type === "argument_list" ? SKIP_CHILDREN8 : null
|
|
13597
14230
|
});
|
|
13598
14231
|
return found;
|
|
13599
14232
|
}
|