dsh-plugin-compat-check 0.1.6 → 0.1.7
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 +17 -30
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/index.js +1295 -550
- package/kernel/lib/api-semantics.mjs +223 -0
- package/kernel/lib/author-ci-budget.mjs +25 -0
- package/kernel/lib/author-ci-output.mjs +22 -0
- package/kernel/lib/author-ci-policy.mjs +6 -1
- package/kernel/lib/baseline.mjs +109 -38
- package/kernel/lib/catalog.mjs +11 -3
- package/kernel/lib/contracts.mjs +16 -1
- package/kernel/lib/execution-environment.mjs +52 -0
- package/kernel/lib/extract.mjs +57 -13
- package/kernel/lib/finding-guidance.mjs +6 -0
- package/kernel/lib/finding-review-store.mjs +11 -23
- package/kernel/lib/issue-report.mjs +2 -1
- package/kernel/lib/patch-mounts.mjs +232 -0
- package/kernel/lib/report-identity-worker.mjs +47 -26
- package/kernel/lib/report-identity.mjs +4 -3
- package/kernel/lib/report-service.mjs +118 -38
- package/kernel/lib/report.mjs +13 -17
- package/kernel/lib/source-analysis.mjs +258 -42
- package/kernel/lib/source-graph.mjs +353 -75
- package/kernel/lib/state-lock.mjs +206 -0
- package/kernel/lib/state-store.mjs +41 -30
- package/kernel/lib/static-input.mjs +111 -0
- package/kernel/lib/syntax.mjs +242 -20
- package/kernel/lib/upgrade-contract.mjs +1 -0
- package/kernel/lib/upgrade-input.mjs +9 -4
- package/kernel/lib/upgrade-preview.mjs +11 -15
- package/kernel/lib/upgrade-worker.mjs +10 -1
- package/kernel/lib/verdict.mjs +28 -5
- package/kernel/lib/version.mjs +2 -34
- package/kernel/lib/worker-analysis.mjs +74 -3
- package/kernel/rules/README.md +30 -208
- package/kernel/rules/dsh-0.2.1-rc.3-review.json +63 -0
- package/kernel/rules/dsh-api-catalog-0.2.1-rc.3.json +743 -0
- package/kernel/rules/severity.json +77 -0
- package/lib/client.js +4668 -3423
- package/lib/finding-guidance.js +6 -0
- package/lib/local-request.js +44 -0
- package/lib/report-contract.js +16 -1
- package/lib/upgrade-contract.js +1 -0
- package/package.json +3 -2
- package/scripts/prepublish-check.mjs +1 -22
- package/scripts/stage-contract.mjs +7 -0
- package/skill/SKILL.md +13 -12
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { parseSource, childNodes } from "./syntax.mjs";
|
|
2
2
|
import { hasEvidence, hasProof, mergedValue } from "./flow-values.mjs";
|
|
3
|
+
import {
|
|
4
|
+
captureSemanticArguments,
|
|
5
|
+
mergeSemanticArguments,
|
|
6
|
+
semanticTarget,
|
|
7
|
+
} from "./api-semantics.mjs";
|
|
3
8
|
|
|
4
9
|
const members = new Set(["MemberExpression", "OptionalMemberExpression"]);
|
|
5
10
|
const calls = new Set(["CallExpression", "OptionalCallExpression"]);
|
|
@@ -85,7 +90,8 @@ export function analyzeSources(
|
|
|
85
90
|
const provideSiteKeys = new Set();
|
|
86
91
|
let scopeId = 0,
|
|
87
92
|
invocationCount = 0,
|
|
88
|
-
constantCalls = 0
|
|
93
|
+
constantCalls = 0,
|
|
94
|
+
semanticGeneration = 0;
|
|
89
95
|
const identifierCache = new WeakMap();
|
|
90
96
|
const guardSets = new Set();
|
|
91
97
|
const guardSet = (values = []) => {
|
|
@@ -95,7 +101,7 @@ export function analyzeSources(
|
|
|
95
101
|
};
|
|
96
102
|
const serviceCache = new WeakMap();
|
|
97
103
|
const stateFor = (value) => {
|
|
98
|
-
if (value?.kind === "object") return value;
|
|
104
|
+
if (value?.kind === "object" || value?.kind === "literal") return value;
|
|
99
105
|
if (value?.kind === "context" || value?.kind === "service")
|
|
100
106
|
return (value.state ||= { fields: new Map(), opaque: false });
|
|
101
107
|
return null;
|
|
@@ -208,6 +214,7 @@ export function analyzeSources(
|
|
|
208
214
|
};
|
|
209
215
|
const writeField = (object, key, value) => {
|
|
210
216
|
noteMutation(object);
|
|
217
|
+
if (object.injectMetadata || object.kind === "literal") markOpaque(object);
|
|
211
218
|
if (journals.length) object.fields = new Map(object.fields);
|
|
212
219
|
object.fields.set(key, value);
|
|
213
220
|
};
|
|
@@ -307,6 +314,7 @@ export function analyzeSources(
|
|
|
307
314
|
};
|
|
308
315
|
modules.set(file, module);
|
|
309
316
|
const callResults = new WeakMap();
|
|
317
|
+
const semanticSnapshots = new WeakMap();
|
|
310
318
|
const effects = new WeakMap();
|
|
311
319
|
const hasEffect = (node, scope) => effects.get(node)?.has(scope.id);
|
|
312
320
|
const effectValue = (node, scope) =>
|
|
@@ -353,6 +361,8 @@ export function analyzeSources(
|
|
|
353
361
|
kind: "object",
|
|
354
362
|
fields: new Map([["exports", cjsExports]]),
|
|
355
363
|
};
|
|
364
|
+
const cjsInjectCandidates = [],
|
|
365
|
+
entryInjectCandidates = [];
|
|
356
366
|
let currentDeferred = null;
|
|
357
367
|
const imported = (spec, name) => ({
|
|
358
368
|
kind: "import",
|
|
@@ -478,8 +488,12 @@ export function analyzeSources(
|
|
|
478
488
|
!args.every((v) => v?.kind === "constant") ||
|
|
479
489
|
!constantOnly(body) ||
|
|
480
490
|
++constantCalls > 512
|
|
481
|
-
)
|
|
491
|
+
) {
|
|
492
|
+
// Ordinary closures are not expanded. Their writes can invalidate
|
|
493
|
+
// scalar/options evidence without changing Context value-flow rules.
|
|
494
|
+
semanticGeneration++;
|
|
482
495
|
return null;
|
|
496
|
+
}
|
|
483
497
|
}
|
|
484
498
|
if (invoking.has(key)) {
|
|
485
499
|
diagnose(
|
|
@@ -577,7 +591,7 @@ export function analyzeSources(
|
|
|
577
591
|
declared.add(service);
|
|
578
592
|
if (kind === "optional-read" || kind === "read")
|
|
579
593
|
softDeclared.add(service);
|
|
580
|
-
|
|
594
|
+
const row = {
|
|
581
595
|
service,
|
|
582
596
|
kind,
|
|
583
597
|
inferred,
|
|
@@ -586,7 +600,9 @@ export function analyzeSources(
|
|
|
586
600
|
start: node.start,
|
|
587
601
|
end: node.end,
|
|
588
602
|
sides: [...(activeSides || sides)],
|
|
589
|
-
}
|
|
603
|
+
};
|
|
604
|
+
dependencies.push(row);
|
|
605
|
+
return row;
|
|
590
606
|
};
|
|
591
607
|
const provide = (service, node) => {
|
|
592
608
|
provided.add(service);
|
|
@@ -617,6 +633,24 @@ export function analyzeSources(
|
|
|
617
633
|
return value === null ? [] : [value];
|
|
618
634
|
});
|
|
619
635
|
};
|
|
636
|
+
// Cordis Inject.resolve treats object keys as service names, not lists
|
|
637
|
+
// named required/optional. Unknown spreads/accessors cannot prove a map.
|
|
638
|
+
const injectServices = (node, scope, value = resolve(node, scope)) => {
|
|
639
|
+
if (value) value.injectMetadata = true;
|
|
640
|
+
if (value?.kind === "literal" && value.complete && !value.opaque)
|
|
641
|
+
return value.strings;
|
|
642
|
+
if (value?.kind === "object" && !value.opaque)
|
|
643
|
+
return [...value.fields.keys()].filter(
|
|
644
|
+
(key) => typeof key === "string",
|
|
645
|
+
);
|
|
646
|
+
diagnose(
|
|
647
|
+
"inject-unresolved",
|
|
648
|
+
file,
|
|
649
|
+
node,
|
|
650
|
+
"Injection declaration cannot be established as an array or service map",
|
|
651
|
+
);
|
|
652
|
+
return [];
|
|
653
|
+
};
|
|
620
654
|
const keyFor = (node, scope, guards) =>
|
|
621
655
|
node?.computed
|
|
622
656
|
? constant(node.property, scope, guards)
|
|
@@ -690,7 +724,7 @@ export function analyzeSources(
|
|
|
690
724
|
node.type,
|
|
691
725
|
)
|
|
692
726
|
)
|
|
693
|
-
return { kind: "constant", value: node.value };
|
|
727
|
+
return { kind: "constant", value: node.value, semanticGeneration };
|
|
694
728
|
if (node.type === "TemplateLiteral") {
|
|
695
729
|
const values = node.expressions.map((n) => resolve(n, scope, guards));
|
|
696
730
|
if (values.every((v) => v?.kind === "constant")) {
|
|
@@ -707,7 +741,14 @@ export function analyzeSources(
|
|
|
707
741
|
);
|
|
708
742
|
return null;
|
|
709
743
|
}
|
|
710
|
-
return {
|
|
744
|
+
return {
|
|
745
|
+
kind: "constant",
|
|
746
|
+
value: chunks.join(""),
|
|
747
|
+
semanticGeneration: values.reduce(
|
|
748
|
+
(min, v) => Math.min(min, v.semanticGeneration ?? -1),
|
|
749
|
+
semanticGeneration,
|
|
750
|
+
),
|
|
751
|
+
};
|
|
711
752
|
}
|
|
712
753
|
}
|
|
713
754
|
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
@@ -727,7 +768,14 @@ export function analyzeSources(
|
|
|
727
768
|
);
|
|
728
769
|
return null;
|
|
729
770
|
}
|
|
730
|
-
return {
|
|
771
|
+
return {
|
|
772
|
+
kind: "constant",
|
|
773
|
+
value: left.value + right.value,
|
|
774
|
+
semanticGeneration: Math.min(
|
|
775
|
+
left.semanticGeneration ?? -1,
|
|
776
|
+
right.semanticGeneration ?? -1,
|
|
777
|
+
),
|
|
778
|
+
};
|
|
731
779
|
}
|
|
732
780
|
}
|
|
733
781
|
if (node.type === "ConditionalExpression") {
|
|
@@ -772,17 +820,51 @@ export function analyzeSources(
|
|
|
772
820
|
}
|
|
773
821
|
if (node.type === "ThisExpression")
|
|
774
822
|
return scope.functionScope().receiver || { kind: "instance" };
|
|
775
|
-
if (node.type === "ArrayExpression")
|
|
776
|
-
|
|
823
|
+
if (node.type === "ArrayExpression") {
|
|
824
|
+
const strings = [];
|
|
825
|
+
let complete = true;
|
|
826
|
+
for (const element of node.elements) {
|
|
827
|
+
const spread = element?.type === "SpreadElement";
|
|
828
|
+
const value = resolve(
|
|
829
|
+
spread ? element.argument : element,
|
|
830
|
+
scope,
|
|
831
|
+
guards,
|
|
832
|
+
);
|
|
833
|
+
if (
|
|
834
|
+
spread &&
|
|
835
|
+
value?.kind === "literal" &&
|
|
836
|
+
value.complete &&
|
|
837
|
+
!value.opaque
|
|
838
|
+
)
|
|
839
|
+
strings.push(...value.strings);
|
|
840
|
+
else if (
|
|
841
|
+
!spread &&
|
|
842
|
+
value?.kind === "constant" &&
|
|
843
|
+
typeof value.value === "string"
|
|
844
|
+
)
|
|
845
|
+
strings.push(value.value);
|
|
846
|
+
else complete = false;
|
|
847
|
+
}
|
|
848
|
+
return {
|
|
849
|
+
kind: "literal",
|
|
850
|
+
strings,
|
|
851
|
+
complete,
|
|
852
|
+
fields: new Map(),
|
|
853
|
+
opaque: false,
|
|
854
|
+
node,
|
|
855
|
+
};
|
|
856
|
+
}
|
|
777
857
|
if (node.type === "ObjectExpression") {
|
|
778
858
|
const fields = new Map();
|
|
779
859
|
for (const p of node.properties)
|
|
780
|
-
if (p.type === "ObjectProperty")
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
);
|
|
785
|
-
|
|
860
|
+
if (p.type === "ObjectProperty") {
|
|
861
|
+
const key = p.computed
|
|
862
|
+
? constant(p.key, scope, guards)
|
|
863
|
+
: keyName(p);
|
|
864
|
+
const value = resolve(p.value, scope, guards);
|
|
865
|
+
if (key === "inject" && value) value.injectMetadata = true;
|
|
866
|
+
fields.set(key, value);
|
|
867
|
+
} else if (p.type === "ObjectMethod")
|
|
786
868
|
fields.set(
|
|
787
869
|
p.computed ? constant(p.key, scope, guards) : keyName(p),
|
|
788
870
|
fnValue(p, scope),
|
|
@@ -793,7 +875,7 @@ export function analyzeSources(
|
|
|
793
875
|
(p.type === "ObjectMethod" && p.kind !== "method") ||
|
|
794
876
|
(p.computed && constant(p.key, scope, guards) === null),
|
|
795
877
|
);
|
|
796
|
-
return { kind: "object", fields, opaque };
|
|
878
|
+
return { kind: "object", fields, opaque, node, semanticGeneration };
|
|
797
879
|
}
|
|
798
880
|
if (members.has(node.type)) {
|
|
799
881
|
const object = transparent(resolve(node.object, scope, guards), node),
|
|
@@ -825,16 +907,14 @@ export function analyzeSources(
|
|
|
825
907
|
if (value?.kind === "context")
|
|
826
908
|
heads.add(text.slice(node.start, node.end));
|
|
827
909
|
return value;
|
|
828
|
-
return null;
|
|
829
910
|
}
|
|
830
911
|
if (calls.has(node.type)) {
|
|
831
912
|
if (
|
|
832
913
|
node.callee.type === "Identifier" &&
|
|
833
914
|
node.callee.name === "require" &&
|
|
834
|
-
!scope.find("require")
|
|
835
|
-
node.arguments[0]?.type === "StringLiteral"
|
|
915
|
+
!scope.find("require")
|
|
836
916
|
)
|
|
837
|
-
return namespace(moduleLinks.get(file)?.get(node.
|
|
917
|
+
return namespace(moduleLinks.get(file)?.get("require@" + node.start));
|
|
838
918
|
let memo = callResults.get(node);
|
|
839
919
|
if (!memo) {
|
|
840
920
|
memo = new Map();
|
|
@@ -853,6 +933,24 @@ export function analyzeSources(
|
|
|
853
933
|
visit(arg, scope, guards);
|
|
854
934
|
return resolve(arg, scope, guards);
|
|
855
935
|
});
|
|
936
|
+
if (callee?.kind === "method") {
|
|
937
|
+
const captured = captureSemanticArguments(
|
|
938
|
+
callee.service,
|
|
939
|
+
callee.method,
|
|
940
|
+
values,
|
|
941
|
+
node.arguments,
|
|
942
|
+
(arg) =>
|
|
943
|
+
arg?.type === "Identifier" &&
|
|
944
|
+
arg.name === "undefined" &&
|
|
945
|
+
!scope.find("undefined"),
|
|
946
|
+
semanticGeneration,
|
|
947
|
+
);
|
|
948
|
+
if (captured) {
|
|
949
|
+
if (!semanticSnapshots.has(node))
|
|
950
|
+
semanticSnapshots.set(node, new Map());
|
|
951
|
+
semanticSnapshots.get(node).set(scope.id, captured);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
856
954
|
if (callee?.kind === "function") {
|
|
857
955
|
const result = callee.invoke(
|
|
858
956
|
values,
|
|
@@ -863,6 +961,17 @@ export function analyzeSources(
|
|
|
863
961
|
memo.set(scope.id, result);
|
|
864
962
|
return result;
|
|
865
963
|
}
|
|
964
|
+
if (
|
|
965
|
+
!callee ||
|
|
966
|
+
!["method", "service"].includes(callee.kind) ||
|
|
967
|
+
(!semanticTarget(callee.service, callee.method) &&
|
|
968
|
+
values.some((v) =>
|
|
969
|
+
["object", "literal", "function"].includes(v?.kind),
|
|
970
|
+
))
|
|
971
|
+
)
|
|
972
|
+
semanticGeneration++;
|
|
973
|
+
if (receiver?.injectMetadata || receiver?.kind === "literal")
|
|
974
|
+
taint(receiver);
|
|
866
975
|
}
|
|
867
976
|
if (
|
|
868
977
|
calls.has(node.type) &&
|
|
@@ -973,6 +1082,7 @@ export function analyzeSources(
|
|
|
973
1082
|
condition = body[0].argument;
|
|
974
1083
|
else if (
|
|
975
1084
|
body[0]?.type === "IfStatement" &&
|
|
1085
|
+
!body[0].alternate &&
|
|
976
1086
|
body[0].consequent.type === "ReturnStatement" &&
|
|
977
1087
|
body[0].consequent.argument?.value === true &&
|
|
978
1088
|
body.at(-1)?.type === "ReturnStatement" &&
|
|
@@ -1096,7 +1206,7 @@ export function analyzeSources(
|
|
|
1096
1206
|
guards.has(`${value.service}.${value.method}`);
|
|
1097
1207
|
if (!callMap.has(value.service)) callMap.set(value.service, new Set());
|
|
1098
1208
|
callMap.get(value.service).add(value.method);
|
|
1099
|
-
|
|
1209
|
+
const site = {
|
|
1100
1210
|
service: value.service,
|
|
1101
1211
|
method: value.method,
|
|
1102
1212
|
plugin: pkgName,
|
|
@@ -1117,7 +1227,9 @@ export function analyzeSources(
|
|
|
1117
1227
|
evidenceSource: "ast",
|
|
1118
1228
|
inferredReceiver: value.receiver?.inferred === true,
|
|
1119
1229
|
sides: [...(activeSides || sides)],
|
|
1120
|
-
}
|
|
1230
|
+
};
|
|
1231
|
+
sites.push(site);
|
|
1232
|
+
return site;
|
|
1121
1233
|
};
|
|
1122
1234
|
const visitFunction = (
|
|
1123
1235
|
node,
|
|
@@ -1346,6 +1458,13 @@ export function analyzeSources(
|
|
|
1346
1458
|
}
|
|
1347
1459
|
if (node.type === "Program")
|
|
1348
1460
|
return statementList(node.body, scope, guards, depth);
|
|
1461
|
+
if (node.type === "StaticBlock")
|
|
1462
|
+
return statementList(
|
|
1463
|
+
node.body,
|
|
1464
|
+
new Scope(scope, ++scopeId, true),
|
|
1465
|
+
guards,
|
|
1466
|
+
depth,
|
|
1467
|
+
);
|
|
1349
1468
|
if (node.type === "BlockStatement")
|
|
1350
1469
|
return statementList(
|
|
1351
1470
|
node.body,
|
|
@@ -1392,15 +1511,20 @@ export function analyzeSources(
|
|
|
1392
1511
|
);
|
|
1393
1512
|
if (node.id.type === "Identifier") {
|
|
1394
1513
|
const field = node.id.name;
|
|
1395
|
-
if (
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1514
|
+
if (
|
|
1515
|
+
injectNames.has(field) ||
|
|
1516
|
+
/^inject(?:Deps|Scope|s)?$/.test(field)
|
|
1517
|
+
) {
|
|
1518
|
+
const kind =
|
|
1519
|
+
scope === root && isEntry && injectNames.has(field)
|
|
1520
|
+
? "required-inject"
|
|
1521
|
+
: "descriptor-inject";
|
|
1522
|
+
const rows = injectServices(node.init, scope, value).map((s) =>
|
|
1523
|
+
dependency(s, kind, node),
|
|
1524
|
+
);
|
|
1525
|
+
if (kind === "required-inject")
|
|
1526
|
+
entryInjectCandidates.push({ value, node, rows });
|
|
1527
|
+
}
|
|
1404
1528
|
if (provideNames.has(field))
|
|
1405
1529
|
for (const s of literalNames(node.init, scope)) {
|
|
1406
1530
|
provide(s, node);
|
|
@@ -1458,7 +1582,16 @@ export function analyzeSources(
|
|
|
1458
1582
|
"Unknown write key may replace existing fields",
|
|
1459
1583
|
);
|
|
1460
1584
|
} else {
|
|
1461
|
-
|
|
1585
|
+
if (
|
|
1586
|
+
node.type === "UnaryExpression" &&
|
|
1587
|
+
node.operator === "delete" &&
|
|
1588
|
+
slot.object.kind === "object"
|
|
1589
|
+
) {
|
|
1590
|
+
noteMutation(state);
|
|
1591
|
+
if (state.injectMetadata) markOpaque(state);
|
|
1592
|
+
if (journals.length) state.fields = new Map(state.fields);
|
|
1593
|
+
state.fields.delete(slot.key);
|
|
1594
|
+
} else writeField(state, slot.key, value);
|
|
1462
1595
|
if (["context", "service"].includes(slot.object.kind))
|
|
1463
1596
|
diagnose(
|
|
1464
1597
|
"flow-service-mutation",
|
|
@@ -1523,11 +1656,19 @@ export function analyzeSources(
|
|
|
1523
1656
|
) {
|
|
1524
1657
|
const result = slot.old.value + right.value;
|
|
1525
1658
|
if (typeof result !== "string" || result.length <= 8192)
|
|
1526
|
-
value = {
|
|
1659
|
+
value = {
|
|
1660
|
+
kind: "constant",
|
|
1661
|
+
value: result,
|
|
1662
|
+
semanticGeneration: Math.min(
|
|
1663
|
+
slot.old.semanticGeneration ?? -1,
|
|
1664
|
+
right.semanticGeneration ?? -1,
|
|
1665
|
+
),
|
|
1666
|
+
};
|
|
1527
1667
|
}
|
|
1528
1668
|
} else if (slot.old?.kind === "constant")
|
|
1529
1669
|
value = {
|
|
1530
1670
|
kind: "constant",
|
|
1671
|
+
semanticGeneration: slot.old.semanticGeneration ?? -1,
|
|
1531
1672
|
value: Number(slot.old.value) + (node.operator === "++" ? 1 : -1),
|
|
1532
1673
|
};
|
|
1533
1674
|
if (!value && node.operator !== "=")
|
|
@@ -1543,18 +1684,28 @@ export function analyzeSources(
|
|
|
1543
1684
|
scope,
|
|
1544
1685
|
node.type === "UpdateExpression" && !node.prefix ? slot.old : value,
|
|
1545
1686
|
);
|
|
1546
|
-
if (node.type === "AssignmentExpression" && slot.key === "inject")
|
|
1547
|
-
|
|
1548
|
-
dependency(name, "descriptor-inject", node)
|
|
1687
|
+
if (node.type === "AssignmentExpression" && slot.key === "inject") {
|
|
1688
|
+
const rows = injectServices(node.right, scope, value).map((name) =>
|
|
1689
|
+
dependency(name, "descriptor-inject", node),
|
|
1690
|
+
);
|
|
1691
|
+
cjsInjectCandidates.push({ owner: slot.object, value, node, rows });
|
|
1692
|
+
}
|
|
1549
1693
|
if (node.type === "AssignmentExpression" && slot.key === "provide")
|
|
1550
1694
|
for (const name of literalNames(node.right, scope)) {
|
|
1551
1695
|
provide(name, node);
|
|
1552
1696
|
}
|
|
1553
1697
|
return;
|
|
1554
1698
|
}
|
|
1555
|
-
if (node.type === "ObjectProperty" && keyName(node) === "inject")
|
|
1556
|
-
|
|
1557
|
-
dependency(
|
|
1699
|
+
if (node.type === "ObjectProperty" && keyName(node) === "inject") {
|
|
1700
|
+
const rows = injectServices(node.value, scope).map((name) =>
|
|
1701
|
+
dependency(name, "descriptor-inject", node),
|
|
1702
|
+
);
|
|
1703
|
+
cjsInjectCandidates.push({
|
|
1704
|
+
value: resolve(node.value, scope),
|
|
1705
|
+
node,
|
|
1706
|
+
rows,
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1558
1709
|
if (node.type === "CatchClause") {
|
|
1559
1710
|
const caught = new Scope(scope, ++scopeId);
|
|
1560
1711
|
bind(node.param, null, caught);
|
|
@@ -1706,8 +1857,10 @@ export function analyzeSources(
|
|
|
1706
1857
|
return;
|
|
1707
1858
|
}
|
|
1708
1859
|
if (calls.has(node.type)) {
|
|
1709
|
-
recordCall(node, scope, guards);
|
|
1860
|
+
const site = recordCall(node, scope, guards);
|
|
1710
1861
|
resolve(node, scope, guards);
|
|
1862
|
+
const captured = semanticSnapshots.get(node)?.get(scope.id);
|
|
1863
|
+
if (site && captured) site.semanticArguments = captured;
|
|
1711
1864
|
let target = resolve(node.callee, scope, guards);
|
|
1712
1865
|
if (target?.kind === "namespace")
|
|
1713
1866
|
target = lookupExport(target.target, "default");
|
|
@@ -1797,7 +1950,16 @@ export function analyzeSources(
|
|
|
1797
1950
|
for (const v of d.declarations)
|
|
1798
1951
|
for (const name of namesIn(v.id))
|
|
1799
1952
|
module.exports.set(name, { local: name });
|
|
1800
|
-
for (const spec of n.specifiers || [])
|
|
1953
|
+
for (const spec of n.specifiers || []) {
|
|
1954
|
+
if (!spec.local) {
|
|
1955
|
+
diagnose(
|
|
1956
|
+
"module-export-uncertain",
|
|
1957
|
+
file,
|
|
1958
|
+
spec,
|
|
1959
|
+
"Namespace re-export binding is not established",
|
|
1960
|
+
);
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1801
1963
|
module.exports.set(
|
|
1802
1964
|
spec.exported.name ?? spec.exported.value,
|
|
1803
1965
|
n.source
|
|
@@ -1807,6 +1969,7 @@ export function analyzeSources(
|
|
|
1807
1969
|
}
|
|
1808
1970
|
: { local: spec.local.name },
|
|
1809
1971
|
);
|
|
1972
|
+
}
|
|
1810
1973
|
}
|
|
1811
1974
|
if (n.type === "ExportAllDeclaration") module.stars.push(n.source.value);
|
|
1812
1975
|
}
|
|
@@ -1817,6 +1980,48 @@ export function analyzeSources(
|
|
|
1817
1980
|
module.cjs = () => cjsModule.fields.get("exports");
|
|
1818
1981
|
module.entry = () => {
|
|
1819
1982
|
if (!isEntry) return;
|
|
1983
|
+
for (const candidate of entryInjectCandidates) {
|
|
1984
|
+
if (!candidate.value?.opaque) continue;
|
|
1985
|
+
diagnose(
|
|
1986
|
+
"inject-unresolved",
|
|
1987
|
+
file,
|
|
1988
|
+
candidate.node,
|
|
1989
|
+
"Injection metadata was mutated or escaped before activation",
|
|
1990
|
+
);
|
|
1991
|
+
for (const row of candidate.rows) {
|
|
1992
|
+
row.kind = "descriptor-inject";
|
|
1993
|
+
row.inferred = true;
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
const exported = module.cjs();
|
|
1997
|
+
const injected = readField(exported, "inject", program);
|
|
1998
|
+
if (injected?.opaque || (exported?.opaque && cjsInjectCandidates.length))
|
|
1999
|
+
diagnose(
|
|
2000
|
+
"inject-unresolved",
|
|
2001
|
+
file,
|
|
2002
|
+
program,
|
|
2003
|
+
"CommonJS injection metadata or its export owner was mutated or escaped",
|
|
2004
|
+
);
|
|
2005
|
+
if (
|
|
2006
|
+
exported?.kind === "object" &&
|
|
2007
|
+
!exported.opaque &&
|
|
2008
|
+
injected &&
|
|
2009
|
+
!injected.opaque
|
|
2010
|
+
) {
|
|
2011
|
+
for (const candidate of cjsInjectCandidates) {
|
|
2012
|
+
const sameOwner =
|
|
2013
|
+
candidate.owner === exported ||
|
|
2014
|
+
exported.node?.properties?.includes(candidate.node);
|
|
2015
|
+
const sameInput =
|
|
2016
|
+
candidate.value === injected ||
|
|
2017
|
+
(injected.node && candidate.value?.node === injected.node);
|
|
2018
|
+
if (!sameOwner || !sameInput) continue;
|
|
2019
|
+
for (const row of candidate.rows) {
|
|
2020
|
+
row.kind = "required-inject";
|
|
2021
|
+
row.inferred = false;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
1820
2025
|
const apply = lookupExport(file, "apply");
|
|
1821
2026
|
if (apply?.kind !== "function") return;
|
|
1822
2027
|
const guards = guardSet();
|
|
@@ -1873,6 +2078,17 @@ export function analyzeSources(
|
|
|
1873
2078
|
const old = unique.get(key);
|
|
1874
2079
|
if (!old) unique.set(key, site);
|
|
1875
2080
|
else {
|
|
2081
|
+
if (old.semanticArguments || site.semanticArguments) {
|
|
2082
|
+
if (old.inferredReceiver && !site.inferredReceiver)
|
|
2083
|
+
old.semanticArguments = site.semanticArguments;
|
|
2084
|
+
else if (!old.inferredReceiver && site.inferredReceiver) {
|
|
2085
|
+
/* Preserve the proved call. */
|
|
2086
|
+
} else
|
|
2087
|
+
old.semanticArguments = mergeSemanticArguments(
|
|
2088
|
+
old.semanticArguments,
|
|
2089
|
+
site.semanticArguments,
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
1876
2092
|
old.inferredReceiver = old.inferredReceiver && site.inferredReceiver;
|
|
1877
2093
|
old.guarded = old.guarded && site.guarded;
|
|
1878
2094
|
old.capabilityGuarded = old.capabilityGuarded && site.capabilityGuarded;
|