gitnexus 1.6.12-rc.2 → 1.6.12-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_shared/graph/types.d.ts +1 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +2 -0
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/languages.d.ts +1 -0
- package/dist/_shared/languages.d.ts.map +1 -1
- package/dist/_shared/languages.js +1 -0
- package/dist/_shared/languages.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +2 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/language-classification.d.ts +2 -1
- package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/language-classification.js +3 -1
- package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts +3 -3
- package/dist/_shared/scope-resolution/registries/class-registry.js +3 -3
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/context.js +2 -0
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.js +2 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
- package/dist/cli/analyze-config.d.ts +2 -2
- package/dist/cli/analyze-config.js +10 -33
- package/dist/cli/doctor.js +6 -4
- package/dist/cli/eval-server.js +13 -1
- package/dist/cli/i18n/en.d.ts +4 -2
- package/dist/cli/i18n/en.js +4 -2
- package/dist/cli/i18n/resources.d.ts +6 -2
- package/dist/cli/i18n/zh-CN.d.ts +2 -0
- package/dist/cli/i18n/zh-CN.js +4 -2
- package/dist/core/analysis-feature-registry.d.ts +1 -1
- package/dist/core/analysis-feature-registry.js +2 -0
- package/dist/core/auto-sync/starter.js +2 -2
- package/dist/core/embeddings/ast-utils.js +8 -3
- package/dist/core/embeddings/chunker.js +67 -10
- package/dist/core/embeddings/embedding-pipeline.d.ts +1 -1
- package/dist/core/embeddings/embedding-pipeline.js +1 -1
- package/dist/core/embeddings/structural-extractor.js +2 -2
- package/dist/core/embeddings/types.d.ts +4 -2
- package/dist/core/embeddings/types.js +18 -0
- package/dist/core/git-ref.d.ts +22 -0
- package/dist/core/git-ref.js +114 -0
- package/dist/core/git-staleness.js +14 -0
- package/dist/core/group/extractors/graphql-extractor.d.ts +1 -1
- package/dist/core/group/extractors/graphql-extractor.js +149 -13
- package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
- package/dist/core/group/extractors/manifest-extractor.js +2 -2
- package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +5 -8
- package/dist/core/ingestion/content-language-classification.d.ts +10 -0
- package/dist/core/ingestion/content-language-classification.js +30 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +1 -0
- package/dist/core/ingestion/filesystem-walker.js +1 -1
- package/dist/core/ingestion/import-resolvers/node-workspace-packages.js +3 -2
- package/dist/core/ingestion/language-config.d.ts +82 -13
- package/dist/core/ingestion/language-config.js +217 -13
- package/dist/core/ingestion/language-provider.d.ts +88 -1
- package/dist/core/ingestion/languages/index.d.ts +7 -0
- package/dist/core/ingestion/languages/index.js +30 -1
- package/dist/core/ingestion/languages/objective-c/analysis-features.d.ts +10 -0
- package/dist/core/ingestion/languages/objective-c/analysis-features.js +19 -0
- package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.d.ts +13 -0
- package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.js +124 -0
- package/dist/core/ingestion/languages/objective-c/facts.d.ts +148 -0
- package/dist/core/ingestion/languages/objective-c/facts.js +1150 -0
- package/dist/core/ingestion/languages/objective-c/import-target.d.ts +11 -0
- package/dist/core/ingestion/languages/objective-c/import-target.js +122 -0
- package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.d.ts +24 -0
- package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.js +261 -0
- package/dist/core/ingestion/languages/objective-c/resolution-config.d.ts +41 -0
- package/dist/core/ingestion/languages/objective-c/resolution-config.js +282 -0
- package/dist/core/ingestion/languages/objective-c/scope-resolver.d.ts +2 -0
- package/dist/core/ingestion/languages/objective-c/scope-resolver.js +503 -0
- package/dist/core/ingestion/languages/objective-c.d.ts +2 -0
- package/dist/core/ingestion/languages/objective-c.js +300 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +31 -0
- package/dist/core/ingestion/languages/typescript/query.js +7 -1
- package/dist/core/ingestion/languages/typescript/tsconfig.js +3 -2
- package/dist/core/ingestion/languages/typescript.js +1 -1
- package/dist/core/ingestion/languages/zig/query.d.ts +26 -0
- package/dist/core/ingestion/languages/zig/query.js +61 -1
- package/dist/core/ingestion/languages/zig/range-binding.d.ts +8 -0
- package/dist/core/ingestion/languages/zig/range-binding.js +24 -0
- package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +3 -2
- package/dist/core/ingestion/languages/zig/scope-resolver.js +18 -5
- package/dist/core/ingestion/languages/zig/this-alias-bindings.d.ts +58 -0
- package/dist/core/ingestion/languages/zig/this-alias-bindings.js +176 -0
- package/dist/core/ingestion/languages/zig/workspace-static-gating.d.ts +8 -0
- package/dist/core/ingestion/languages/zig/workspace-static-gating.js +82 -0
- package/dist/core/ingestion/model/registration-table.d.ts +3 -3
- package/dist/core/ingestion/model/registration-table.js +9 -5
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
- package/dist/core/ingestion/model/symbol-table.js +2 -0
- package/dist/core/ingestion/model/type-registry.d.ts +1 -1
- package/dist/core/ingestion/parsing-processor.d.ts +22 -6
- package/dist/core/ingestion/parsing-processor.js +39 -20
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +9 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +307 -96
- package/dist/core/ingestion/pipeline-phases/parse-round-budget.d.ts +42 -0
- package/dist/core/ingestion/pipeline-phases/parse-round-budget.js +50 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +7 -1
- package/dist/core/ingestion/scope-extractor.js +4 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +13 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +2 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +2 -0
- package/dist/core/ingestion/scope-resolution/passes/property-dispatch.d.ts +78 -5
- package/dist/core/ingestion/scope-resolution/passes/property-dispatch.js +262 -7
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +5 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +10 -1
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +39 -3
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +82 -3
- package/dist/core/ingestion/scope-resolution/value-ref-edges.d.ts +14 -0
- package/dist/core/ingestion/scope-resolution/value-ref-edges.js +14 -0
- package/dist/core/ingestion/tree-sitter-queries.js +2 -0
- package/dist/core/ingestion/utils/symbol-labels.js +2 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -1
- package/dist/core/ingestion/workers/parse-worker.js +63 -3
- package/dist/core/ingestion/workers/worker-pool.d.ts +56 -2
- package/dist/core/ingestion/workers/worker-pool.js +117 -26
- package/dist/core/lbug/csv-generator.js +2 -0
- package/dist/core/lbug/schema.d.ts +3 -1
- package/dist/core/lbug/schema.js +27 -0
- package/dist/core/search/fts-indexes.js +13 -4
- package/dist/core/search/fts-schema.js +2 -0
- package/dist/core/tree-sitter/parser-loader.js +10 -0
- package/dist/core/tree-sitter/vendored-grammars.d.ts +1 -1
- package/dist/core/tree-sitter/vendored-grammars.js +2 -1
- package/dist/mcp/local/local-backend.d.ts +33 -0
- package/dist/mcp/local/local-backend.js +166 -5
- package/dist/mcp/tools.js +6 -4
- package/dist/server/analyze-job.d.ts +19 -1
- package/dist/server/analyze-job.js +14 -3
- package/dist/server/analyze-launch.d.ts +14 -0
- package/dist/server/analyze-launch.js +59 -14
- package/dist/server/analyze-worker-ipc.d.ts +6 -5
- package/dist/server/analyze-worker-ipc.js +4 -0
- package/dist/server/api.js +68 -21
- package/dist/server/git-clone.d.ts +38 -5
- package/dist/server/git-clone.js +216 -38
- package/dist/server/repo-projection.d.ts +88 -0
- package/dist/server/repo-projection.js +53 -0
- package/dist/storage/file-lock.js +5 -3
- package/dist/storage/parse-cache.js +32 -1
- package/dist/storage/parsedfile-store.js +53 -1
- package/dist/storage/v8-sidecar.d.ts +11 -0
- package/dist/storage/v8-sidecar.js +15 -7
- package/dist/utils/process-identity.d.ts +13 -0
- package/dist/utils/process-identity.js +18 -0
- package/package.json +1 -1
- package/scripts/build-tree-sitter-grammars.cjs +4 -3
- package/vendor/tree-sitter-objc/LICENSE +21 -0
- package/vendor/tree-sitter-objc/README.md +18 -0
- package/vendor/tree-sitter-objc/binding.gyp +35 -0
- package/vendor/tree-sitter-objc/bindings/node/binding.cc +19 -0
- package/vendor/tree-sitter-objc/bindings/node/binding_test.js +9 -0
- package/vendor/tree-sitter-objc/bindings/node/index.d.ts +27 -0
- package/vendor/tree-sitter-objc/bindings/node/index.js +11 -0
- package/vendor/tree-sitter-objc/grammar.js +1286 -0
- package/vendor/tree-sitter-objc/package.json +56 -0
- package/vendor/tree-sitter-objc/prebuilds/SHA256SUMS +6 -0
- package/vendor/tree-sitter-objc/prebuilds/darwin-arm64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/darwin-x64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/linux-arm64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/linux-x64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/win32-arm64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/win32-x64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/queries/folds.scm +20 -0
- package/vendor/tree-sitter-objc/queries/highlights.scm +216 -0
- package/vendor/tree-sitter-objc/queries/indents.scm +1 -0
- package/vendor/tree-sitter-objc/queries/injections.scm +10 -0
- package/vendor/tree-sitter-objc/queries/locals.scm +1 -0
- package/vendor/tree-sitter-objc/src/grammar.json +16292 -0
- package/vendor/tree-sitter-objc/src/node-types.json +7524 -0
- package/vendor/tree-sitter-objc/src/parser.c +684023 -0
- package/vendor/tree-sitter-objc/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-objc/src/tree_sitter/array.h +291 -0
- package/vendor/tree-sitter-objc/src/tree_sitter/parser.h +266 -0
- package/vendor/tree-sitter-objc/tree-sitter-objc.wasm +0 -0
- package/vendor/tree-sitter-objc/tree-sitter.json +38 -0
- package/web/assets/{agent-BMRqXorA.js → agent-C2n33ANd.js} +1 -1
- package/web/assets/{index-Dj3vQOHK.js → index-CHJoW_P1.js} +18 -18
- package/web/assets/src-jc8Ffy3T.js +1 -0
- package/web/index.html +2 -2
- package/web/assets/src-Df5C1Nz4.js +0 -1
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { SupportedLanguages, } from '../../../../_shared/index.js';
|
|
2
|
+
import { generateId } from '../../../../lib/utils.js';
|
|
3
|
+
import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
|
|
4
|
+
import { objectiveCProvider } from '../objective-c.js';
|
|
5
|
+
import { populateObjectiveCCompilationUnitSiblings } from './compilation-unit-siblings.js';
|
|
6
|
+
import { resolveObjectiveCImportTarget } from './import-target.js';
|
|
7
|
+
import { loadObjectiveCResolutionConfig } from './resolution-config.js';
|
|
8
|
+
import { applyObjectiveCCaptureSideChannel, isInternalObjectiveCFunctionDef, objcClassQualifiedName, objcProtocolQualifiedName, objcUnresolvedMessageQualifiedName, objectiveCFactsFromParsedFiles, parseObjCType, } from './facts.js';
|
|
9
|
+
export const objectiveCScopeResolver = {
|
|
10
|
+
language: SupportedLanguages.ObjectiveC,
|
|
11
|
+
languageProvider: objectiveCProvider,
|
|
12
|
+
importEdgeReason: 'objective-c-scope: import',
|
|
13
|
+
loadResolutionConfig: (repoPath) => loadObjectiveCResolutionConfig(repoPath),
|
|
14
|
+
resolveImportTarget: (targetRaw, fromFile, allFilePaths, resolutionConfig) => resolveObjectiveCImportTarget(targetRaw, fromFile, allFilePaths, resolutionConfig),
|
|
15
|
+
mergeBindings: (existing, incoming) => [...existing, ...incoming],
|
|
16
|
+
arityCompatibility: (callsite, def) => {
|
|
17
|
+
if (callsite.arity === undefined || def.parameterCount === undefined)
|
|
18
|
+
return 'unknown';
|
|
19
|
+
return callsite.arity === def.parameterCount ? 'compatible' : 'incompatible';
|
|
20
|
+
},
|
|
21
|
+
buildMro: () => new Map(),
|
|
22
|
+
applyCaptureSideChannel: applyObjectiveCCaptureSideChannel,
|
|
23
|
+
populateOwners: (parsed) => populateClassOwnedMembers(parsed),
|
|
24
|
+
populateNamespaceSiblings: populateObjectiveCCompilationUnitSiblings,
|
|
25
|
+
isFileLocalDef: (def) => isInternalObjectiveCFunctionDef(def),
|
|
26
|
+
isSuperReceiver: (receiverText) => receiverText.trim() === 'super',
|
|
27
|
+
fieldFallbackOnMethodLookup: false,
|
|
28
|
+
propagatesReturnTypesAcrossImports: false,
|
|
29
|
+
collapseMemberCallsByCallerTarget: true,
|
|
30
|
+
emitPostResolutionEdges(graph, parsedFiles) {
|
|
31
|
+
const facts = objectiveCFactsFromParsedFiles(parsedFiles);
|
|
32
|
+
if (facts.length === 0)
|
|
33
|
+
return;
|
|
34
|
+
const workspace = buildObjectiveCWorkspaceFacts(facts);
|
|
35
|
+
for (const fact of facts) {
|
|
36
|
+
emitObjectiveCHeritageEdges(graph, fact, workspace);
|
|
37
|
+
emitObjectiveCCategoryEdges(graph, fact, workspace);
|
|
38
|
+
emitObjectiveCImplementationEvidence(graph, fact);
|
|
39
|
+
emitObjectiveCUnresolvedMessageEvidence(graph, fact);
|
|
40
|
+
emitObjectiveCMessageEdges(graph, fact, workspace);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
function graphNodeId(label, qualifiedName) {
|
|
45
|
+
return generateId(label, qualifiedName);
|
|
46
|
+
}
|
|
47
|
+
function relationshipId(type, sourceId, targetId, reason) {
|
|
48
|
+
return generateId(type, `${sourceId}->${targetId}:${reason}`);
|
|
49
|
+
}
|
|
50
|
+
function addRelationship(graph, type, sourceId, targetId, reason, confidence = 0.9) {
|
|
51
|
+
graph.addRelationship({
|
|
52
|
+
id: relationshipId(type, sourceId, targetId, reason),
|
|
53
|
+
sourceId,
|
|
54
|
+
targetId,
|
|
55
|
+
type,
|
|
56
|
+
confidence,
|
|
57
|
+
reason,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function labelForContainer(container) {
|
|
61
|
+
return container.label;
|
|
62
|
+
}
|
|
63
|
+
function buildObjectiveCWorkspaceFacts(facts) {
|
|
64
|
+
const containersByQualifiedName = new Map();
|
|
65
|
+
const classByName = new Map();
|
|
66
|
+
const protocolsByName = new Map();
|
|
67
|
+
const methodsByExactOwner = new Map();
|
|
68
|
+
const methodsByDispatchOwner = new Map();
|
|
69
|
+
const memberTypesByOwner = new Map();
|
|
70
|
+
const classProtocols = new Map();
|
|
71
|
+
const protocolParents = new Map();
|
|
72
|
+
const superclassByClass = new Map();
|
|
73
|
+
for (const fileFact of facts) {
|
|
74
|
+
for (const container of fileFact.containers) {
|
|
75
|
+
const existing = containersByQualifiedName.get(container.qualifiedName);
|
|
76
|
+
containersByQualifiedName.set(container.qualifiedName, mergeContainerFacts(existing, container));
|
|
77
|
+
if (container.kind === 'class') {
|
|
78
|
+
classByName.set(container.name, container);
|
|
79
|
+
if (container.superclass !== undefined)
|
|
80
|
+
superclassByClass.set(container.name, container.superclass);
|
|
81
|
+
if (container.protocols.length > 0) {
|
|
82
|
+
let protocols = classProtocols.get(container.name);
|
|
83
|
+
if (protocols === undefined) {
|
|
84
|
+
protocols = new Set();
|
|
85
|
+
classProtocols.set(container.name, protocols);
|
|
86
|
+
}
|
|
87
|
+
for (const protocol of container.protocols)
|
|
88
|
+
protocols.add(protocol);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else if (container.kind === 'protocol') {
|
|
92
|
+
protocolsByName.set(container.name, container);
|
|
93
|
+
if (container.protocols.length > 0) {
|
|
94
|
+
let parents = protocolParents.get(container.name);
|
|
95
|
+
if (parents === undefined) {
|
|
96
|
+
parents = new Set();
|
|
97
|
+
protocolParents.set(container.name, parents);
|
|
98
|
+
}
|
|
99
|
+
for (const protocol of container.protocols)
|
|
100
|
+
parents.add(protocol);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (container.hostClass !== undefined) {
|
|
104
|
+
if (container.protocols.length > 0) {
|
|
105
|
+
let protocols = classProtocols.get(container.hostClass);
|
|
106
|
+
if (protocols === undefined) {
|
|
107
|
+
protocols = new Set();
|
|
108
|
+
classProtocols.set(container.hostClass, protocols);
|
|
109
|
+
}
|
|
110
|
+
for (const protocol of container.protocols)
|
|
111
|
+
protocols.add(protocol);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const method of fileFact.methods) {
|
|
116
|
+
appendMap(methodsByExactOwner, method.ownerQualifiedName, method);
|
|
117
|
+
appendMap(methodsByDispatchOwner, method.ownerQualifiedName, method);
|
|
118
|
+
if (method.hostClass !== undefined) {
|
|
119
|
+
appendMap(methodsByDispatchOwner, objcClassQualifiedName(method.hostClass), method);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const member of fileFact.members) {
|
|
123
|
+
addMemberType(memberTypesByOwner, member.ownerQualifiedName, member);
|
|
124
|
+
if (member.hostClass !== undefined) {
|
|
125
|
+
addMemberType(memberTypesByOwner, objcClassQualifiedName(member.hostClass), member);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const hierarchyWorkspace = { protocolParents };
|
|
130
|
+
const classNames = new Set([...classByName.keys(), ...classProtocols.keys()]);
|
|
131
|
+
const classesByProtocol = new Map();
|
|
132
|
+
for (const className of classNames) {
|
|
133
|
+
const seenClasses = new Set();
|
|
134
|
+
let currentClass = className;
|
|
135
|
+
while (currentClass !== undefined && !seenClasses.has(currentClass)) {
|
|
136
|
+
seenClasses.add(currentClass);
|
|
137
|
+
const directProtocols = classProtocols.get(currentClass);
|
|
138
|
+
if (directProtocols !== undefined) {
|
|
139
|
+
for (const directProtocol of directProtocols) {
|
|
140
|
+
for (const protocolName of protocolHierarchy(hierarchyWorkspace, directProtocol)) {
|
|
141
|
+
let implementers = classesByProtocol.get(protocolName);
|
|
142
|
+
if (implementers === undefined) {
|
|
143
|
+
implementers = [];
|
|
144
|
+
classesByProtocol.set(protocolName, implementers);
|
|
145
|
+
}
|
|
146
|
+
implementers.push(className);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
currentClass = superclassByClass.get(currentClass);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const classesByProtocolFrozen = new Map();
|
|
154
|
+
for (const [protocolName, implementers] of classesByProtocol) {
|
|
155
|
+
classesByProtocolFrozen.set(protocolName, Object.freeze([...new Set(implementers)].sort((left, right) => left.localeCompare(right))));
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
containersByQualifiedName,
|
|
159
|
+
classByName,
|
|
160
|
+
protocolsByName,
|
|
161
|
+
methodsByDispatchOwner,
|
|
162
|
+
methodsByExactOwner,
|
|
163
|
+
memberTypesByOwner,
|
|
164
|
+
classProtocols,
|
|
165
|
+
protocolParents,
|
|
166
|
+
superclassByClass,
|
|
167
|
+
classesByProtocol: classesByProtocolFrozen,
|
|
168
|
+
protocolImplementationBySelector: new Map(),
|
|
169
|
+
emittedProtocolCandidateSets: new Set(),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function addMemberType(memberTypesByOwner, ownerQualifiedName, member) {
|
|
173
|
+
const type = parseObjCType(member.declaredType);
|
|
174
|
+
if (type === undefined)
|
|
175
|
+
return;
|
|
176
|
+
let types = memberTypesByOwner.get(ownerQualifiedName);
|
|
177
|
+
if (types === undefined) {
|
|
178
|
+
types = new Map();
|
|
179
|
+
memberTypesByOwner.set(ownerQualifiedName, types);
|
|
180
|
+
}
|
|
181
|
+
types.set(member.name, type);
|
|
182
|
+
}
|
|
183
|
+
function mergeContainerFacts(existing, incoming) {
|
|
184
|
+
if (existing === undefined)
|
|
185
|
+
return incoming;
|
|
186
|
+
const protocols = Array.from(new Set([...existing.protocols, ...incoming.protocols])).sort();
|
|
187
|
+
return {
|
|
188
|
+
...existing,
|
|
189
|
+
declarationRole: existing.declarationRole === 'implementation' || incoming.declarationRole === 'implementation'
|
|
190
|
+
? 'implementation'
|
|
191
|
+
: 'interface',
|
|
192
|
+
startLine: Math.min(existing.startLine, incoming.startLine),
|
|
193
|
+
endLine: Math.max(existing.endLine, incoming.endLine),
|
|
194
|
+
...(existing.superclass !== undefined || incoming.superclass !== undefined
|
|
195
|
+
? { superclass: existing.superclass ?? incoming.superclass }
|
|
196
|
+
: {}),
|
|
197
|
+
protocols,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function appendMap(map, key, value) {
|
|
201
|
+
const existing = map.get(key);
|
|
202
|
+
if (existing === undefined)
|
|
203
|
+
map.set(key, [value]);
|
|
204
|
+
else
|
|
205
|
+
existing.push(value);
|
|
206
|
+
}
|
|
207
|
+
function emitObjectiveCHeritageEdges(graph, facts, workspace) {
|
|
208
|
+
for (const container of facts.containers) {
|
|
209
|
+
const sourceId = graphNodeId(labelForContainer(container), container.qualifiedName);
|
|
210
|
+
if (container.kind === 'class' && container.superclass !== undefined) {
|
|
211
|
+
const superclass = workspace.classByName.get(container.superclass);
|
|
212
|
+
if (superclass !== undefined) {
|
|
213
|
+
addRelationship(graph, 'EXTENDS', sourceId, graphNodeId('Class', superclass.qualifiedName), 'objc: superclass');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const protocolSourceId = container.hostClass !== undefined
|
|
217
|
+
? graphNodeId('Class', objcClassQualifiedName(container.hostClass))
|
|
218
|
+
: sourceId;
|
|
219
|
+
for (const protocolName of container.protocols) {
|
|
220
|
+
const protocol = workspace.protocolsByName.get(protocolName);
|
|
221
|
+
if (protocol === undefined)
|
|
222
|
+
continue;
|
|
223
|
+
addRelationship(graph, 'IMPLEMENTS', protocolSourceId, graphNodeId('Protocol', protocol.qualifiedName), 'objc: protocol conformance');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function emitObjectiveCCategoryEdges(graph, facts, workspace) {
|
|
228
|
+
for (const container of facts.containers) {
|
|
229
|
+
if (container.hostClass === undefined)
|
|
230
|
+
continue;
|
|
231
|
+
if (!workspace.classByName.has(container.hostClass))
|
|
232
|
+
continue;
|
|
233
|
+
addRelationship(graph, 'MEMBER_OF', graphNodeId('Category', container.qualifiedName), graphNodeId('Class', objcClassQualifiedName(container.hostClass)), 'objc: category host class');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function emitObjectiveCUnresolvedMessageEvidence(graph, facts) {
|
|
237
|
+
for (const unresolved of facts.unresolvedMessages) {
|
|
238
|
+
const evidenceId = graphNodeId('CodeElement', objcUnresolvedMessageQualifiedName(facts.filePath, unresolved.startLine, unresolved.startCol, unresolved.selector));
|
|
239
|
+
addRelationship(graph, 'USES', unresolved.sourceMethodId, evidenceId, `objc-message: unresolved: ${unresolved.reason}`, 0.5);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function emitObjectiveCImplementationEvidence(graph, facts) {
|
|
243
|
+
for (const container of facts.containers) {
|
|
244
|
+
if (container.declarationRole !== 'implementation')
|
|
245
|
+
continue;
|
|
246
|
+
const targetId = graphNodeId(labelForContainer(container), container.qualifiedName);
|
|
247
|
+
emitImplementationEvidence(graph, facts.filePath, targetId, `@implementation ${container.name}`, `objc:implementation:${container.qualifiedName}:${facts.filePath}:${container.startLine}`, container.startLine, container.endLine, {
|
|
248
|
+
objectiveCKind: 'implementation-evidence',
|
|
249
|
+
implementationKind: container.kind,
|
|
250
|
+
targetQualifiedName: container.qualifiedName,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
for (const method of facts.methods) {
|
|
254
|
+
if (method.declarationRole !== 'implementation')
|
|
255
|
+
continue;
|
|
256
|
+
emitImplementationEvidence(graph, facts.filePath, method.nodeId, `${method.methodKind}[${method.ownerName} ${method.selector}] implementation`, `objc:method-implementation:${method.qualifiedName}:${facts.filePath}:${method.startLine}`, method.startLine, method.endLine, {
|
|
257
|
+
objectiveCKind: 'implementation-evidence',
|
|
258
|
+
implementationKind: 'method',
|
|
259
|
+
targetQualifiedName: method.qualifiedName,
|
|
260
|
+
selector: method.selector,
|
|
261
|
+
methodKind: method.methodKind,
|
|
262
|
+
objectiveCOwner: method.ownerQualifiedName,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function emitImplementationEvidence(graph, filePath, targetId, name, qualifiedName, startLine, endLine, extras) {
|
|
267
|
+
const nodeId = graphNodeId('CodeElement', qualifiedName);
|
|
268
|
+
graph.addNode({
|
|
269
|
+
id: nodeId,
|
|
270
|
+
label: 'CodeElement',
|
|
271
|
+
properties: {
|
|
272
|
+
name,
|
|
273
|
+
qualifiedName,
|
|
274
|
+
filePath,
|
|
275
|
+
startLine,
|
|
276
|
+
endLine,
|
|
277
|
+
language: SupportedLanguages.ObjectiveC,
|
|
278
|
+
isExported: false,
|
|
279
|
+
...extras,
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
addRelationship(graph, 'DEFINES', graphNodeId('File', filePath), nodeId, 'objc: implementation evidence', 1);
|
|
283
|
+
addRelationship(graph, 'DECLARES', nodeId, targetId, 'objc: implementation of merged symbol', 1);
|
|
284
|
+
}
|
|
285
|
+
function emitObjectiveCMessageEdges(graph, facts, workspace) {
|
|
286
|
+
for (const message of facts.messages) {
|
|
287
|
+
const targets = resolveMessageTargets(message, workspace);
|
|
288
|
+
if (targets.kind === 'none')
|
|
289
|
+
continue;
|
|
290
|
+
if (targets.kind === 'category-collision') {
|
|
291
|
+
emitCategoryDispatchEvidence(graph, facts, message, targets.candidates);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (targets.kind === 'protocol') {
|
|
295
|
+
emitProtocolMessageEvidence(graph, facts, message, targets.protocolName, targets.candidates, workspace);
|
|
296
|
+
}
|
|
297
|
+
for (const target of targets.methods) {
|
|
298
|
+
if (graph.getNode(target.nodeId) === undefined)
|
|
299
|
+
continue;
|
|
300
|
+
addRelationship(graph, 'CALLS', message.sourceMethodId, target.nodeId, targets.kind === 'protocol'
|
|
301
|
+
? 'objc-message: protocol receiver'
|
|
302
|
+
: `objc-message: ${message.receiverKind} receiver`, targets.kind === 'protocol' ? 0.8 : 0.9);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function messageTargetsFromDispatchLookup(lookup) {
|
|
307
|
+
return lookup.categoryCollision
|
|
308
|
+
? { kind: 'category-collision', candidates: lookup.methods }
|
|
309
|
+
: { kind: 'direct', methods: lookup.methods };
|
|
310
|
+
}
|
|
311
|
+
function resolveMessageTargets(message, workspace) {
|
|
312
|
+
if (message.receiverKind === 'dynamic') {
|
|
313
|
+
return { kind: 'none', methods: [] };
|
|
314
|
+
}
|
|
315
|
+
if (message.receiverKind === 'class') {
|
|
316
|
+
const className = message.receiverType?.name ?? message.receiverText;
|
|
317
|
+
return messageTargetsFromDispatchLookup(findDispatchMethods(workspace, className, '+', message.selector));
|
|
318
|
+
}
|
|
319
|
+
if (message.receiverKind === 'self') {
|
|
320
|
+
const owner = workspace.containersByQualifiedName.get(message.sourceOwnerQualifiedName);
|
|
321
|
+
const className = owner?.hostClass ?? owner?.name ?? message.sourceOwnerName;
|
|
322
|
+
if (owner?.kind === 'protocol') {
|
|
323
|
+
return {
|
|
324
|
+
kind: 'direct',
|
|
325
|
+
methods: findProtocolMethods(workspace, owner.name, message.sourceMethodKind, message.selector),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return messageTargetsFromDispatchLookup(findDispatchMethods(workspace, className, message.sourceMethodKind, message.selector));
|
|
329
|
+
}
|
|
330
|
+
if (message.receiverKind === 'super') {
|
|
331
|
+
const owner = workspace.containersByQualifiedName.get(message.sourceOwnerQualifiedName);
|
|
332
|
+
const className = owner?.hostClass ?? owner?.name ?? message.sourceOwnerName;
|
|
333
|
+
const superclass = workspace.superclassByClass.get(className);
|
|
334
|
+
return superclass === undefined
|
|
335
|
+
? { kind: 'none', methods: [] }
|
|
336
|
+
: messageTargetsFromDispatchLookup(findDispatchMethods(workspace, superclass, message.sourceMethodKind, message.selector));
|
|
337
|
+
}
|
|
338
|
+
const receiverType = message.receiverType ?? resolveMemberReceiverType(message, workspace);
|
|
339
|
+
if (receiverType?.kind === 'dynamic' || receiverType?.kind === 'class-object') {
|
|
340
|
+
return { kind: 'none', methods: [] };
|
|
341
|
+
}
|
|
342
|
+
if (message.receiverKind === 'unknown' && receiverType === undefined) {
|
|
343
|
+
return { kind: 'none', methods: [] };
|
|
344
|
+
}
|
|
345
|
+
if (receiverType?.kind === 'class' && receiverType.name !== undefined) {
|
|
346
|
+
return messageTargetsFromDispatchLookup(findDispatchMethods(workspace, receiverType.name, '-', message.selector));
|
|
347
|
+
}
|
|
348
|
+
if (receiverType?.kind === 'protocol' && receiverType.name !== undefined) {
|
|
349
|
+
const methods = findProtocolMethods(workspace, receiverType.name, '-', message.selector);
|
|
350
|
+
const candidates = findProtocolImplementationCandidates(workspace, receiverType.name, message.selector);
|
|
351
|
+
return {
|
|
352
|
+
kind: 'protocol',
|
|
353
|
+
protocolName: receiverType.name,
|
|
354
|
+
methods,
|
|
355
|
+
candidates,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return { kind: 'none', methods: [] };
|
|
359
|
+
}
|
|
360
|
+
function resolveMemberReceiverType(message, workspace) {
|
|
361
|
+
if (message.receiverMemberName === undefined)
|
|
362
|
+
return undefined;
|
|
363
|
+
const owner = workspace.containersByQualifiedName.get(message.sourceOwnerQualifiedName);
|
|
364
|
+
let className = owner?.hostClass ?? owner?.name ?? message.sourceOwnerName;
|
|
365
|
+
const seen = new Set();
|
|
366
|
+
while (className !== undefined && !seen.has(className)) {
|
|
367
|
+
seen.add(className);
|
|
368
|
+
const type = workspace.memberTypesByOwner
|
|
369
|
+
.get(objcClassQualifiedName(className))
|
|
370
|
+
?.get(message.receiverMemberName);
|
|
371
|
+
if (type !== undefined)
|
|
372
|
+
return type;
|
|
373
|
+
className = workspace.superclassByClass.get(className);
|
|
374
|
+
}
|
|
375
|
+
return workspace.memberTypesByOwner
|
|
376
|
+
.get(message.sourceOwnerQualifiedName)
|
|
377
|
+
?.get(message.receiverMemberName);
|
|
378
|
+
}
|
|
379
|
+
function findDispatchMethods(workspace, className, methodKind, selector) {
|
|
380
|
+
const seen = new Set();
|
|
381
|
+
let currentClass = className;
|
|
382
|
+
while (currentClass !== undefined && !seen.has(currentClass)) {
|
|
383
|
+
seen.add(currentClass);
|
|
384
|
+
const ownerQn = objcClassQualifiedName(currentClass);
|
|
385
|
+
const methods = uniqueMethods((workspace.methodsByDispatchOwner.get(ownerQn) ?? []).filter((method) => method.methodKind === methodKind && method.selector === selector));
|
|
386
|
+
if (methods.length > 0) {
|
|
387
|
+
return {
|
|
388
|
+
methods,
|
|
389
|
+
// Named categories can replace a host-class implementation at runtime,
|
|
390
|
+
// but image-load order is not statically knowable. Preserve all targets
|
|
391
|
+
// as evidence instead of claiming that each is a certain CALLS edge.
|
|
392
|
+
categoryCollision: methods.length > 1 && methods.some((method) => method.ownerKind === 'category'),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
currentClass = workspace.superclassByClass.get(currentClass);
|
|
396
|
+
}
|
|
397
|
+
return { methods: [], categoryCollision: false };
|
|
398
|
+
}
|
|
399
|
+
function findExactOwnerMethods(workspace, ownerQualifiedName, methodKind, selector) {
|
|
400
|
+
return (workspace.methodsByExactOwner.get(ownerQualifiedName) ?? []).filter((method) => method.methodKind === methodKind && method.selector === selector);
|
|
401
|
+
}
|
|
402
|
+
function findProtocolMethods(workspace, protocolName, methodKind, selector) {
|
|
403
|
+
for (const name of protocolHierarchy(workspace, protocolName)) {
|
|
404
|
+
const methods = findExactOwnerMethods(workspace, objcProtocolQualifiedName(name), methodKind, selector);
|
|
405
|
+
if (methods.length > 0)
|
|
406
|
+
return uniqueMethods(methods);
|
|
407
|
+
}
|
|
408
|
+
return [];
|
|
409
|
+
}
|
|
410
|
+
function protocolHierarchy(workspace, protocolName) {
|
|
411
|
+
const seen = new Set();
|
|
412
|
+
const pending = [protocolName];
|
|
413
|
+
const hierarchy = [];
|
|
414
|
+
while (pending.length > 0) {
|
|
415
|
+
const current = pending.shift();
|
|
416
|
+
if (current === undefined || seen.has(current))
|
|
417
|
+
continue;
|
|
418
|
+
seen.add(current);
|
|
419
|
+
hierarchy.push(current);
|
|
420
|
+
const parents = workspace.protocolParents.get(current);
|
|
421
|
+
if (parents === undefined)
|
|
422
|
+
continue;
|
|
423
|
+
pending.push(...[...parents].sort());
|
|
424
|
+
}
|
|
425
|
+
return hierarchy;
|
|
426
|
+
}
|
|
427
|
+
function uniqueMethods(methods) {
|
|
428
|
+
return [...new Map(methods.map((method) => [method.nodeId, method])).values()].sort((left, right) => left.qualifiedName.localeCompare(right.qualifiedName));
|
|
429
|
+
}
|
|
430
|
+
function findProtocolImplementationCandidates(workspace, protocolName, selector) {
|
|
431
|
+
const cacheKey = `${protocolName}\0${selector}`;
|
|
432
|
+
const cached = workspace.protocolImplementationBySelector.get(cacheKey);
|
|
433
|
+
if (cached !== undefined)
|
|
434
|
+
return cached;
|
|
435
|
+
const out = [];
|
|
436
|
+
for (const className of workspace.classesByProtocol.get(protocolName) ?? []) {
|
|
437
|
+
out.push(...findDispatchMethods(workspace, className, '-', selector).methods);
|
|
438
|
+
}
|
|
439
|
+
const result = uniqueMethods(out);
|
|
440
|
+
workspace.protocolImplementationBySelector.set(cacheKey, result);
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
function emitCategoryDispatchEvidence(graph, facts, message, candidates) {
|
|
444
|
+
if (candidates.length === 0)
|
|
445
|
+
return;
|
|
446
|
+
const qualifiedName = `objc:category-dispatch-candidates:${facts.filePath}:` +
|
|
447
|
+
`${message.startLine}:${message.startCol}:${message.selector}`;
|
|
448
|
+
const nodeId = graphNodeId('CodeElement', qualifiedName);
|
|
449
|
+
graph.addNode({
|
|
450
|
+
id: nodeId,
|
|
451
|
+
label: 'CodeElement',
|
|
452
|
+
properties: {
|
|
453
|
+
name: `[${message.receiverText} ${message.selector}] category dispatch candidates`,
|
|
454
|
+
qualifiedName,
|
|
455
|
+
filePath: facts.filePath,
|
|
456
|
+
startLine: message.startLine,
|
|
457
|
+
endLine: message.startLine,
|
|
458
|
+
language: SupportedLanguages.ObjectiveC,
|
|
459
|
+
isExported: false,
|
|
460
|
+
objectiveCKind: 'category-dispatch-candidates',
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
addRelationship(graph, 'DEFINES', graphNodeId('File', facts.filePath), nodeId, `objc: category dispatch candidate evidence: ${message.selector}`, 1);
|
|
464
|
+
addRelationship(graph, 'USES', message.sourceMethodId, nodeId, `objc-message: category dispatch candidates: ${message.selector}`, 0.7);
|
|
465
|
+
for (const candidate of candidates) {
|
|
466
|
+
if (graph.getNode(candidate.nodeId) === undefined)
|
|
467
|
+
continue;
|
|
468
|
+
addRelationship(graph, 'USES', nodeId, candidate.nodeId, `objc-category-dispatch-candidate: ${message.selector}`, 0.5);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function emitProtocolMessageEvidence(graph, facts, message, protocolName, candidates, workspace) {
|
|
472
|
+
if (candidates.length === 0)
|
|
473
|
+
return;
|
|
474
|
+
const setKey = `${protocolName}\0${message.selector}`;
|
|
475
|
+
const qualifiedName = `objc:protocol-candidates:${protocolName}:${message.selector}`;
|
|
476
|
+
const nodeId = graphNodeId('CodeElement', qualifiedName);
|
|
477
|
+
const protocol = workspace.protocolsByName.get(protocolName);
|
|
478
|
+
const evidenceFilePath = protocol?.filePath ?? facts.filePath;
|
|
479
|
+
if (!workspace.emittedProtocolCandidateSets.has(setKey)) {
|
|
480
|
+
workspace.emittedProtocolCandidateSets.add(setKey);
|
|
481
|
+
graph.addNode({
|
|
482
|
+
id: nodeId,
|
|
483
|
+
label: 'CodeElement',
|
|
484
|
+
properties: {
|
|
485
|
+
name: `[id<${protocolName}> ${message.selector}] protocol implementers`,
|
|
486
|
+
qualifiedName,
|
|
487
|
+
filePath: evidenceFilePath,
|
|
488
|
+
startLine: protocol?.startLine ?? message.startLine,
|
|
489
|
+
endLine: protocol?.endLine ?? message.startLine,
|
|
490
|
+
language: SupportedLanguages.ObjectiveC,
|
|
491
|
+
isExported: false,
|
|
492
|
+
objectiveCKind: 'protocol-implementers',
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
addRelationship(graph, 'DEFINES', graphNodeId('File', evidenceFilePath), nodeId, `objc: protocol receiver candidate evidence: ${protocolName} ${message.selector}`, 1);
|
|
496
|
+
for (const candidate of candidates) {
|
|
497
|
+
if (graph.getNode(candidate.nodeId) === undefined)
|
|
498
|
+
continue;
|
|
499
|
+
addRelationship(graph, 'USES', nodeId, candidate.nodeId, `objc-protocol-candidate: ${protocolName} ${message.selector}`, 0.5);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
addRelationship(graph, 'USES', message.sourceMethodId, nodeId, `objc-message: protocol receiver candidates: ${protocolName} ${message.selector}`, 0.7);
|
|
503
|
+
}
|