gitnexus 1.5.3 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/_shared/graph/types.d.ts +1 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +1 -0
- package/dist/_shared/index.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 +3 -1
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/mro-strategy.d.ts +19 -0
- package/dist/_shared/mro-strategy.d.ts.map +1 -0
- package/dist/_shared/mro-strategy.js +2 -0
- package/dist/_shared/mro-strategy.js.map +1 -0
- package/dist/cli/ai-context.d.ts +1 -0
- package/dist/cli/ai-context.js +28 -4
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +30 -4
- package/dist/cli/group.d.ts +2 -0
- package/dist/cli/group.js +233 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/serve.js +4 -1
- package/dist/cli/setup.js +34 -3
- package/dist/config/ignore-service.js +8 -3
- package/dist/core/augmentation/engine.js +1 -1
- package/dist/core/git-staleness.d.ts +13 -0
- package/dist/core/git-staleness.js +29 -0
- package/dist/core/group/bridge-db.d.ts +82 -0
- package/dist/core/group/bridge-db.js +460 -0
- package/dist/core/group/bridge-schema.d.ts +27 -0
- package/dist/core/group/bridge-schema.js +55 -0
- package/dist/core/group/config-parser.d.ts +3 -0
- package/dist/core/group/config-parser.js +83 -0
- package/dist/core/group/contract-extractor.d.ts +7 -0
- package/dist/core/group/contract-extractor.js +1 -0
- package/dist/core/group/extractors/fs-utils.d.ts +10 -0
- package/dist/core/group/extractors/fs-utils.js +24 -0
- package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
- package/dist/core/group/extractors/grpc-extractor.js +386 -0
- package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
- package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
- package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
- package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
- package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
- package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
- package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
- package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
- package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/go.js +215 -0
- package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
- package/dist/core/group/extractors/http-patterns/index.js +44 -0
- package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/java.js +253 -0
- package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/http-patterns/node.js +354 -0
- package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/php.js +70 -0
- package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/python.js +133 -0
- package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
- package/dist/core/group/extractors/http-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
- package/dist/core/group/extractors/http-route-extractor.js +391 -0
- package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
- package/dist/core/group/extractors/manifest-extractor.js +235 -0
- package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
- package/dist/core/group/extractors/topic-extractor.js +97 -0
- package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/go.js +120 -0
- package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
- package/dist/core/group/extractors/topic-patterns/index.js +38 -0
- package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/java.js +80 -0
- package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/topic-patterns/node.js +155 -0
- package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/python.js +116 -0
- package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
- package/dist/core/group/extractors/topic-patterns/types.js +10 -0
- package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
- package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
- package/dist/core/group/matching.d.ts +13 -0
- package/dist/core/group/matching.js +198 -0
- package/dist/core/group/normalization.d.ts +3 -0
- package/dist/core/group/normalization.js +115 -0
- package/dist/core/group/service-boundary-detector.d.ts +8 -0
- package/dist/core/group/service-boundary-detector.js +155 -0
- package/dist/core/group/service.d.ts +46 -0
- package/dist/core/group/service.js +160 -0
- package/dist/core/group/storage.d.ts +9 -0
- package/dist/core/group/storage.js +91 -0
- package/dist/core/group/sync.d.ts +21 -0
- package/dist/core/group/sync.js +148 -0
- package/dist/core/group/types.d.ts +130 -0
- package/dist/core/group/types.js +1 -0
- package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
- package/dist/core/ingestion/binding-accumulator.js +336 -0
- package/dist/core/ingestion/call-processor.d.ts +155 -24
- package/dist/core/ingestion/call-processor.js +1129 -247
- package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/generic.js +135 -0
- package/dist/core/ingestion/class-types.d.ts +34 -0
- package/dist/core/ingestion/class-types.js +1 -0
- package/dist/core/ingestion/cobol-processor.d.ts +1 -1
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
- package/dist/core/ingestion/entry-point-scoring.js +1 -0
- package/dist/core/ingestion/field-types.d.ts +2 -2
- package/dist/core/ingestion/filesystem-walker.js +8 -0
- package/dist/core/ingestion/framework-detection.d.ts +1 -0
- package/dist/core/ingestion/framework-detection.js +1 -0
- package/dist/core/ingestion/heritage-processor.d.ts +8 -15
- package/dist/core/ingestion/heritage-processor.js +15 -28
- package/dist/core/ingestion/import-processor.d.ts +1 -11
- package/dist/core/ingestion/import-processor.js +1 -13
- package/dist/core/ingestion/import-resolvers/utils.js +1 -0
- package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/vue.js +9 -0
- package/dist/core/ingestion/language-config.js +1 -1
- package/dist/core/ingestion/language-provider.d.ts +14 -3
- package/dist/core/ingestion/languages/c-cpp.js +168 -1
- package/dist/core/ingestion/languages/csharp.js +20 -0
- package/dist/core/ingestion/languages/dart.js +26 -4
- package/dist/core/ingestion/languages/go.js +22 -0
- package/dist/core/ingestion/languages/index.d.ts +1 -0
- package/dist/core/ingestion/languages/index.js +2 -0
- package/dist/core/ingestion/languages/java.js +17 -0
- package/dist/core/ingestion/languages/kotlin.js +24 -1
- package/dist/core/ingestion/languages/php.js +23 -11
- package/dist/core/ingestion/languages/python.js +9 -0
- package/dist/core/ingestion/languages/ruby.js +43 -0
- package/dist/core/ingestion/languages/rust.js +38 -0
- package/dist/core/ingestion/languages/swift.js +31 -0
- package/dist/core/ingestion/languages/typescript.d.ts +1 -0
- package/dist/core/ingestion/languages/typescript.js +52 -3
- package/dist/core/ingestion/languages/vue.d.ts +13 -0
- package/dist/core/ingestion/languages/vue.js +81 -0
- package/dist/core/ingestion/markdown-processor.d.ts +1 -1
- package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
- package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
- package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +14 -4
- package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
- package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
- package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
- package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
- package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
- package/dist/core/ingestion/method-extractors/generic.js +84 -17
- package/dist/core/ingestion/method-types.d.ts +29 -0
- package/dist/core/ingestion/model/field-registry.d.ts +18 -0
- package/dist/core/ingestion/model/field-registry.js +22 -0
- package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
- package/dist/core/ingestion/model/heritage-map.js +159 -0
- package/dist/core/ingestion/model/index.d.ts +20 -0
- package/dist/core/ingestion/model/index.js +41 -0
- package/dist/core/ingestion/model/method-registry.d.ts +62 -0
- package/dist/core/ingestion/model/method-registry.js +130 -0
- package/dist/core/ingestion/model/registration-table.d.ts +139 -0
- package/dist/core/ingestion/model/registration-table.js +224 -0
- package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
- package/dist/core/ingestion/model/resolution-context.js +337 -0
- package/dist/core/ingestion/model/resolve.d.ts +56 -0
- package/dist/core/ingestion/model/resolve.js +297 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
- package/dist/core/ingestion/model/semantic-model.js +120 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
- package/dist/core/ingestion/model/symbol-table.js +206 -0
- package/dist/core/ingestion/model/type-registry.d.ts +39 -0
- package/dist/core/ingestion/model/type-registry.js +62 -0
- package/dist/core/ingestion/mro-processor.d.ts +5 -4
- package/dist/core/ingestion/mro-processor.js +311 -107
- package/dist/core/ingestion/parsing-processor.d.ts +5 -4
- package/dist/core/ingestion/parsing-processor.js +224 -87
- package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
- package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/index.js +22 -0
- package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
- package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
- package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
- package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
- package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
- package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
- package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
- package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
- package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
- package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
- package/dist/core/ingestion/pipeline-phases/types.js +37 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +35 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +174 -0
- package/dist/core/ingestion/pipeline.d.ts +18 -10
- package/dist/core/ingestion/pipeline.js +66 -1410
- package/dist/core/ingestion/process-processor.js +1 -1
- package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
- package/dist/core/ingestion/tree-sitter-queries.js +90 -0
- package/dist/core/ingestion/type-env.d.ts +15 -2
- package/dist/core/ingestion/type-env.js +163 -102
- package/dist/core/ingestion/type-extractors/csharp.js +17 -0
- package/dist/core/ingestion/type-extractors/jvm.js +11 -0
- package/dist/core/ingestion/type-extractors/php.js +0 -55
- package/dist/core/ingestion/type-extractors/ruby.js +0 -32
- package/dist/core/ingestion/type-extractors/swift.js +13 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
- package/dist/core/ingestion/type-extractors/typescript.js +66 -69
- package/dist/core/ingestion/utils/ast-helpers.d.ts +32 -44
- package/dist/core/ingestion/utils/ast-helpers.js +157 -573
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +10 -0
- package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
- package/dist/core/ingestion/utils/graph-sort.js +100 -0
- package/dist/core/ingestion/utils/method-props.d.ts +32 -0
- package/dist/core/ingestion/utils/method-props.js +147 -0
- package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
- package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
- package/dist/core/ingestion/workers/parse-worker.js +469 -200
- package/dist/core/lbug/lbug-adapter.d.ts +6 -0
- package/dist/core/lbug/lbug-adapter.js +134 -27
- package/dist/core/lbug/pool-adapter.d.ts +76 -0
- package/dist/core/lbug/pool-adapter.js +522 -0
- package/dist/core/run-analyze.d.ts +2 -0
- package/dist/core/run-analyze.js +1 -1
- package/dist/core/search/bm25-index.js +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -0
- package/dist/core/wiki/graph-queries.js +1 -1
- package/dist/mcp/core/embedder.js +6 -5
- package/dist/mcp/core/lbug-adapter.d.ts +3 -63
- package/dist/mcp/core/lbug-adapter.js +3 -484
- package/dist/mcp/local/local-backend.d.ts +31 -2
- package/dist/mcp/local/local-backend.js +255 -46
- package/dist/mcp/resources.js +5 -4
- package/dist/mcp/staleness.d.ts +3 -13
- package/dist/mcp/staleness.js +2 -31
- package/dist/mcp/tools.js +80 -4
- package/dist/server/analyze-job.d.ts +2 -0
- package/dist/server/analyze-job.js +4 -0
- package/dist/server/api.d.ts +20 -1
- package/dist/server/api.js +306 -71
- package/dist/server/git-clone.d.ts +2 -1
- package/dist/server/git-clone.js +98 -5
- package/dist/storage/git.d.ts +13 -0
- package/dist/storage/git.js +25 -0
- package/dist/storage/repo-manager.js +1 -1
- package/package.json +9 -3
- package/scripts/patch-tree-sitter-swift.cjs +78 -0
- package/vendor/tree-sitter-proto/binding.gyp +30 -0
- package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-proto/package.json +18 -0
- package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
- package/vendor/tree-sitter-proto/src/parser.c +10149 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
- package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
- package/dist/core/ingestion/named-binding-processor.js +0 -42
- package/dist/core/ingestion/resolution-context.d.ts +0 -58
- package/dist/core/ingestion/resolution-context.js +0 -135
- package/dist/core/ingestion/symbol-table.d.ts +0 -79
- package/dist/core/ingestion/symbol-table.js +0 -115
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic Resolution Functions
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that resolve methods across the inheritance hierarchy
|
|
5
|
+
* using only the SemanticModel registries and HeritageMap — NO dependency
|
|
6
|
+
* on resolution-context.ts (circular dependency risk).
|
|
7
|
+
*/
|
|
8
|
+
import type { SymbolDefinition } from './symbol-table.js';
|
|
9
|
+
import type { SemanticModel } from './semantic-model.js';
|
|
10
|
+
import type { HeritageMap } from './heritage-map.js';
|
|
11
|
+
import type { MroStrategy } from '../../../_shared/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* Gather all ancestor IDs in BFS / topological order.
|
|
14
|
+
* Returns the linearized list of ancestor IDs (excluding the class itself).
|
|
15
|
+
*/
|
|
16
|
+
declare function gatherAncestors(classId: string, parentMap: Map<string, string[]>): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Compute C3 linearization for a class given a parentMap.
|
|
19
|
+
* Returns an array of ancestor IDs in C3 order (excluding the class itself),
|
|
20
|
+
* or null if linearization fails (inconsistent or cyclic hierarchy).
|
|
21
|
+
*
|
|
22
|
+
* Used internally by `lookupMethodByOwnerWithMRO` for the Python MRO
|
|
23
|
+
* strategy and re-exported for mro-processor.ts (graph-level MRO emission).
|
|
24
|
+
*/
|
|
25
|
+
export declare function c3Linearize(classId: string, parentMap: Map<string, string[]>, cache: Map<string, string[] | null>, inProgress?: Set<string>): string[] | null;
|
|
26
|
+
export { gatherAncestors };
|
|
27
|
+
/**
|
|
28
|
+
* Look up a method on an owner class, walking the parent chain via HeritageMap
|
|
29
|
+
* when the method isn't found on the direct owner.
|
|
30
|
+
*
|
|
31
|
+
* Respects the 5 per-language MRO strategies:
|
|
32
|
+
* - `first-wins`: BFS ancestor walk, first match wins (default)
|
|
33
|
+
* - `leftmost-base`: BFS ancestor walk, leftmost base in declaration order wins (C++);
|
|
34
|
+
* HeritageMap preserves insertion order matching source declaration,
|
|
35
|
+
* so BFS order is equivalent to leftmost-base semantics
|
|
36
|
+
* - `c3`: C3-linearized ancestor order, first match wins (Python)
|
|
37
|
+
* - `implements-split`: BFS ancestor walk, first match wins (Java/C#) —
|
|
38
|
+
* full ambiguity detection for multiple interface defaults
|
|
39
|
+
* is handled by computeMRO at graph level
|
|
40
|
+
* - `qualified-syntax`: No auto-resolution (Rust) — returns undefined
|
|
41
|
+
*
|
|
42
|
+
* Uses the `c3Linearize` defined in this file (also consumed by
|
|
43
|
+
* mro-processor.ts for graph-level MRO emission) for the `c3` strategy.
|
|
44
|
+
*
|
|
45
|
+
* Depends only on {@link SemanticModel} + {@link HeritageMap} + an
|
|
46
|
+
* {@link MroStrategy} literal — NO dependency on SymbolTable, the language
|
|
47
|
+
* registry, or resolution-context, which keeps the `model/` module free of
|
|
48
|
+
* cross-layer imports. Callers derive the strategy from their language
|
|
49
|
+
* provider before invoking this function.
|
|
50
|
+
*
|
|
51
|
+
* @internal This is the low-level MRO walker. Exported so call-processor's
|
|
52
|
+
* higher-level resolvers (and unit tests) can invoke it directly. Callers
|
|
53
|
+
* outside `core/ingestion/` should use the higher-level resolvers in
|
|
54
|
+
* call-processor.ts instead of depending on this function.
|
|
55
|
+
*/
|
|
56
|
+
export declare const lookupMethodByOwnerWithMRO: (ownerNodeId: string, methodName: string, heritageMap: HeritageMap, model: SemanticModel, strategy: MroStrategy, argCount?: number) => SymbolDefinition | undefined;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic Resolution Functions
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that resolve methods across the inheritance hierarchy
|
|
5
|
+
* using only the SemanticModel registries and HeritageMap — NO dependency
|
|
6
|
+
* on resolution-context.ts (circular dependency risk).
|
|
7
|
+
*/
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// MRO primitives.
|
|
10
|
+
//
|
|
11
|
+
// `c3Linearize` and its BFS helper `gatherAncestors` live here so the model
|
|
12
|
+
// layer stays a pure leaf — mro-processor.ts (graph-level MRO emission)
|
|
13
|
+
// imports `c3Linearize` from this file.
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* Gather all ancestor IDs in BFS / topological order.
|
|
17
|
+
* Returns the linearized list of ancestor IDs (excluding the class itself).
|
|
18
|
+
*/
|
|
19
|
+
function gatherAncestors(classId, parentMap) {
|
|
20
|
+
const visited = new Set();
|
|
21
|
+
const order = [];
|
|
22
|
+
const queue = [...(parentMap.get(classId) ?? [])];
|
|
23
|
+
while (queue.length > 0) {
|
|
24
|
+
const id = queue.shift();
|
|
25
|
+
if (visited.has(id))
|
|
26
|
+
continue;
|
|
27
|
+
visited.add(id);
|
|
28
|
+
order.push(id);
|
|
29
|
+
const grandparents = parentMap.get(id);
|
|
30
|
+
if (grandparents) {
|
|
31
|
+
for (const gp of grandparents) {
|
|
32
|
+
if (!visited.has(gp))
|
|
33
|
+
queue.push(gp);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return order;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compute C3 linearization for a class given a parentMap.
|
|
41
|
+
* Returns an array of ancestor IDs in C3 order (excluding the class itself),
|
|
42
|
+
* or null if linearization fails (inconsistent or cyclic hierarchy).
|
|
43
|
+
*
|
|
44
|
+
* Used internally by `lookupMethodByOwnerWithMRO` for the Python MRO
|
|
45
|
+
* strategy and re-exported for mro-processor.ts (graph-level MRO emission).
|
|
46
|
+
*/
|
|
47
|
+
export function c3Linearize(classId, parentMap, cache, inProgress) {
|
|
48
|
+
if (cache.has(classId))
|
|
49
|
+
return cache.get(classId);
|
|
50
|
+
// Iterative C3 linearization using an explicit work stack. The recursive
|
|
51
|
+
// version overflows the call stack on deep class hierarchies (10K+
|
|
52
|
+
// levels in large Android/Java codebases).
|
|
53
|
+
//
|
|
54
|
+
// Strategy: maintain a stack of { classId, phase } frames. Each frame
|
|
55
|
+
// goes through two phases:
|
|
56
|
+
// ENTER (0) – check cache / cycle, push parent frames to compute first
|
|
57
|
+
// MERGE (1) – all parent linearizations are cached, merge them C3-style
|
|
58
|
+
const visiting = inProgress ?? new Set();
|
|
59
|
+
const ENTER = 0;
|
|
60
|
+
const MERGE = 1;
|
|
61
|
+
const stack = [{ id: classId, phase: ENTER }];
|
|
62
|
+
while (stack.length > 0) {
|
|
63
|
+
const frame = stack[stack.length - 1];
|
|
64
|
+
if (frame.phase === ENTER) {
|
|
65
|
+
// ── ENTER phase ─────────────────────────────────────────────
|
|
66
|
+
if (cache.has(frame.id)) {
|
|
67
|
+
stack.pop();
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (visiting.has(frame.id)) {
|
|
71
|
+
// Cycle detected
|
|
72
|
+
cache.set(frame.id, null);
|
|
73
|
+
stack.pop();
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
visiting.add(frame.id);
|
|
77
|
+
const directParents = parentMap.get(frame.id);
|
|
78
|
+
if (!directParents || directParents.length === 0) {
|
|
79
|
+
visiting.delete(frame.id);
|
|
80
|
+
cache.set(frame.id, []);
|
|
81
|
+
stack.pop();
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// Switch to MERGE phase and push parents that still need computing
|
|
85
|
+
frame.phase = MERGE;
|
|
86
|
+
let allParentsCached = true;
|
|
87
|
+
for (let i = directParents.length - 1; i >= 0; i--) {
|
|
88
|
+
const pid = directParents[i];
|
|
89
|
+
if (!cache.has(pid)) {
|
|
90
|
+
stack.push({ id: pid, phase: ENTER });
|
|
91
|
+
allParentsCached = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// If all parents are already cached, proceed directly to the MERGE
|
|
95
|
+
// phase below (frame.phase is already MERGE, frame is at stack top).
|
|
96
|
+
// Otherwise, loop back to process the newly-pushed parent frames first.
|
|
97
|
+
if (!allParentsCached) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// ── MERGE phase ───────────────────────────────────────────────
|
|
102
|
+
// directParents is guaranteed non-empty here — the ENTER phase already
|
|
103
|
+
// handles the empty-parents case and pops the frame before switching
|
|
104
|
+
// to MERGE.
|
|
105
|
+
stack.pop();
|
|
106
|
+
const directParents = parentMap.get(frame.id);
|
|
107
|
+
// Build parent linearizations from cache
|
|
108
|
+
const parentLinearizations = [];
|
|
109
|
+
let failed = false;
|
|
110
|
+
for (const pid of directParents) {
|
|
111
|
+
const pLin = cache.get(pid);
|
|
112
|
+
if (pLin === undefined) {
|
|
113
|
+
// Should not happen if phases are ordered correctly, but guard anyway
|
|
114
|
+
failed = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
if (pLin === null) {
|
|
118
|
+
// Parent linearization failed (cycle or inconsistent)
|
|
119
|
+
failed = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
parentLinearizations.push([pid, ...pLin]);
|
|
123
|
+
}
|
|
124
|
+
if (failed) {
|
|
125
|
+
visiting.delete(frame.id);
|
|
126
|
+
cache.set(frame.id, null);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
// Add the direct parents list as the final sequence
|
|
130
|
+
const sequences = [...parentLinearizations, [...directParents]];
|
|
131
|
+
const result = [];
|
|
132
|
+
let inconsistent = false;
|
|
133
|
+
while (sequences.some((s) => s.length > 0)) {
|
|
134
|
+
// Find a good head: one that doesn't appear in the tail of any other sequence
|
|
135
|
+
let head = null;
|
|
136
|
+
for (const seq of sequences) {
|
|
137
|
+
if (seq.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
const candidate = seq[0];
|
|
140
|
+
const inTail = sequences.some((other) => other.length > 1 && other.indexOf(candidate, 1) !== -1);
|
|
141
|
+
if (!inTail) {
|
|
142
|
+
head = candidate;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (head === null) {
|
|
147
|
+
inconsistent = true;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
result.push(head);
|
|
151
|
+
// Remove the chosen head from all sequences
|
|
152
|
+
for (const seq of sequences) {
|
|
153
|
+
if (seq.length > 0 && seq[0] === head) {
|
|
154
|
+
seq.shift();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
visiting.delete(frame.id);
|
|
159
|
+
cache.set(frame.id, inconsistent ? null : result);
|
|
160
|
+
}
|
|
161
|
+
return cache.get(classId) ?? null;
|
|
162
|
+
}
|
|
163
|
+
// `gatherAncestors` is exported so mro-processor.ts can reuse the same
|
|
164
|
+
// BFS traversal for graph-level MRO emission.
|
|
165
|
+
export { gatherAncestors };
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// C3 linearization cache (per HeritageMap, auto-drained via WeakMap)
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
/**
|
|
170
|
+
* Per-HeritageMap cache of C3 linearization results keyed by owner nodeId.
|
|
171
|
+
*
|
|
172
|
+
* HeritageMap instances are immutable after construction, so C3 output is
|
|
173
|
+
* stable for the lifetime of a HeritageMap. WeakMap lets the cache auto-drain
|
|
174
|
+
* when the HeritageMap is garbage collected (end of ingestion run), so we
|
|
175
|
+
* never need to manually invalidate it.
|
|
176
|
+
*
|
|
177
|
+
* `null` is a sentinel for "C3 failed for this owner" (cyclic or inconsistent
|
|
178
|
+
* hierarchy) so we don't re-run the expensive linearization repeatedly.
|
|
179
|
+
*/
|
|
180
|
+
const c3LinearizationCache = new WeakMap();
|
|
181
|
+
const getCachedC3Linearization = (ownerNodeId, heritageMap) => {
|
|
182
|
+
let perHmCache = c3LinearizationCache.get(heritageMap);
|
|
183
|
+
if (!perHmCache) {
|
|
184
|
+
perHmCache = new Map();
|
|
185
|
+
c3LinearizationCache.set(heritageMap, perHmCache);
|
|
186
|
+
}
|
|
187
|
+
const cached = perHmCache.get(ownerNodeId);
|
|
188
|
+
if (cached !== undefined)
|
|
189
|
+
return cached;
|
|
190
|
+
const parentMap = buildParentMapFromHeritage(ownerNodeId, heritageMap);
|
|
191
|
+
const result = c3Linearize(ownerNodeId, parentMap, new Map()) ?? null;
|
|
192
|
+
perHmCache.set(ownerNodeId, result);
|
|
193
|
+
return result;
|
|
194
|
+
};
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Heritage → parentMap conversion
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
/**
|
|
199
|
+
* Build a parentMap from HeritageMap for use with c3Linearize.
|
|
200
|
+
* Traverses the parent chain starting from startNodeId, collecting all
|
|
201
|
+
* parent→children relationships into a Map<string, string[]>.
|
|
202
|
+
*
|
|
203
|
+
* Uses a head-pointer BFS (queue[head++]) instead of Array.shift() to avoid
|
|
204
|
+
* O(n) per-dequeue re-indexing. For wide/shallow hierarchies common in
|
|
205
|
+
* large Java/C# codebases this keeps the walk linear in ancestor count.
|
|
206
|
+
*/
|
|
207
|
+
const buildParentMapFromHeritage = (startNodeId, heritageMap) => {
|
|
208
|
+
const parentMap = new Map();
|
|
209
|
+
const visited = new Set();
|
|
210
|
+
const queue = [startNodeId];
|
|
211
|
+
let head = 0;
|
|
212
|
+
while (head < queue.length) {
|
|
213
|
+
const nodeId = queue[head++];
|
|
214
|
+
if (visited.has(nodeId))
|
|
215
|
+
continue;
|
|
216
|
+
visited.add(nodeId);
|
|
217
|
+
const parents = heritageMap.getParents(nodeId);
|
|
218
|
+
if (parents.length > 0) {
|
|
219
|
+
parentMap.set(nodeId, parents);
|
|
220
|
+
for (const p of parents) {
|
|
221
|
+
if (!visited.has(p))
|
|
222
|
+
queue.push(p);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return parentMap;
|
|
227
|
+
};
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
// MRO-aware method lookup
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
/**
|
|
232
|
+
* Look up a method on an owner class, walking the parent chain via HeritageMap
|
|
233
|
+
* when the method isn't found on the direct owner.
|
|
234
|
+
*
|
|
235
|
+
* Respects the 5 per-language MRO strategies:
|
|
236
|
+
* - `first-wins`: BFS ancestor walk, first match wins (default)
|
|
237
|
+
* - `leftmost-base`: BFS ancestor walk, leftmost base in declaration order wins (C++);
|
|
238
|
+
* HeritageMap preserves insertion order matching source declaration,
|
|
239
|
+
* so BFS order is equivalent to leftmost-base semantics
|
|
240
|
+
* - `c3`: C3-linearized ancestor order, first match wins (Python)
|
|
241
|
+
* - `implements-split`: BFS ancestor walk, first match wins (Java/C#) —
|
|
242
|
+
* full ambiguity detection for multiple interface defaults
|
|
243
|
+
* is handled by computeMRO at graph level
|
|
244
|
+
* - `qualified-syntax`: No auto-resolution (Rust) — returns undefined
|
|
245
|
+
*
|
|
246
|
+
* Uses the `c3Linearize` defined in this file (also consumed by
|
|
247
|
+
* mro-processor.ts for graph-level MRO emission) for the `c3` strategy.
|
|
248
|
+
*
|
|
249
|
+
* Depends only on {@link SemanticModel} + {@link HeritageMap} + an
|
|
250
|
+
* {@link MroStrategy} literal — NO dependency on SymbolTable, the language
|
|
251
|
+
* registry, or resolution-context, which keeps the `model/` module free of
|
|
252
|
+
* cross-layer imports. Callers derive the strategy from their language
|
|
253
|
+
* provider before invoking this function.
|
|
254
|
+
*
|
|
255
|
+
* @internal This is the low-level MRO walker. Exported so call-processor's
|
|
256
|
+
* higher-level resolvers (and unit tests) can invoke it directly. Callers
|
|
257
|
+
* outside `core/ingestion/` should use the higher-level resolvers in
|
|
258
|
+
* call-processor.ts instead of depending on this function.
|
|
259
|
+
*/
|
|
260
|
+
export const lookupMethodByOwnerWithMRO = (ownerNodeId, methodName, heritageMap, model, strategy, argCount) => {
|
|
261
|
+
// Direct lookup first (child override — no walk needed).
|
|
262
|
+
// argCount is threaded through so arity-differing overloads on the direct
|
|
263
|
+
// owner can be disambiguated before the MRO walk starts.
|
|
264
|
+
const direct = model.methods.lookupMethodByOwner(ownerNodeId, methodName, argCount);
|
|
265
|
+
if (direct)
|
|
266
|
+
return direct;
|
|
267
|
+
// Rust: requires qualified syntax (<Type as Trait>::method), no auto-resolution
|
|
268
|
+
if (strategy === 'qualified-syntax')
|
|
269
|
+
return undefined;
|
|
270
|
+
// Determine ancestor walk order based on MRO strategy.
|
|
271
|
+
// readonly to accept the cached (frozen) c3 linearization without copying.
|
|
272
|
+
let ancestors;
|
|
273
|
+
if (strategy === 'c3') {
|
|
274
|
+
// C3 linearization (memoized per HeritageMap
|
|
275
|
+
// so repeated calls for the same owner within an ingestion run reuse the
|
|
276
|
+
// linearization instead of rebuilding the parent map and re-running C3).
|
|
277
|
+
// c3Linearize returns ancestors only (excludes the owner itself),
|
|
278
|
+
// matching heritageMap.getAncestors() semantics.
|
|
279
|
+
const c3Result = getCachedC3Linearization(ownerNodeId, heritageMap);
|
|
280
|
+
// Fall back to BFS order if C3 fails (cyclic or inconsistent hierarchy).
|
|
281
|
+
// Note: BFS order may not preserve Python MRO semantics in these edge
|
|
282
|
+
// cases, but cyclic/inconsistent hierarchies are invalid in Python anyway.
|
|
283
|
+
ancestors = c3Result ?? heritageMap.getAncestors(ownerNodeId);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
// first-wins, leftmost-base, implements-split: BFS order via HeritageMap
|
|
287
|
+
ancestors = heritageMap.getAncestors(ownerNodeId);
|
|
288
|
+
}
|
|
289
|
+
// Walk ancestors in MRO order — first match wins.
|
|
290
|
+
// argCount narrows overloaded ancestors the same way as the direct lookup.
|
|
291
|
+
for (const ancestorId of ancestors) {
|
|
292
|
+
const method = model.methods.lookupMethodByOwner(ancestorId, methodName, argCount);
|
|
293
|
+
if (method)
|
|
294
|
+
return method;
|
|
295
|
+
}
|
|
296
|
+
return undefined;
|
|
297
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Model
|
|
3
|
+
*
|
|
4
|
+
* Top-level orchestrator for all resolution-time data. Owns:
|
|
5
|
+
*
|
|
6
|
+
* - Three owner-scoped registries (types, methods, fields)
|
|
7
|
+
* - A nested SymbolTable (file + callable name indexes) wrapped so
|
|
8
|
+
* that `add()` fans out into the registries via the dispatch table
|
|
9
|
+
*
|
|
10
|
+
* ## Dependency direction
|
|
11
|
+
*
|
|
12
|
+
* gitnexus-shared (NodeLabel) — leaf
|
|
13
|
+
* ↑
|
|
14
|
+
* symbol-table.ts — pure file/callable index
|
|
15
|
+
* ↑
|
|
16
|
+
* model/type-registry / method-registry / field-registry
|
|
17
|
+
* ↑
|
|
18
|
+
* model/registration-table.ts — dispatch table factory
|
|
19
|
+
* ↑
|
|
20
|
+
* model/semantic-model.ts — THIS FILE (orchestrator)
|
|
21
|
+
* ↑
|
|
22
|
+
* resolve.ts, call-processor.ts, resolution-context.ts, ...
|
|
23
|
+
*
|
|
24
|
+
* `symbol-table.ts` is a leaf — it never imports from `./model/`. This
|
|
25
|
+
* file (semantic-model.ts) is the ONLY place where SymbolTable and the
|
|
26
|
+
* owner-scoped registries are composed. Upstream consumers pass around
|
|
27
|
+
* the `SemanticModel` interface and reach into `.symbols` for file-scoped
|
|
28
|
+
* operations or `.types` / `.methods` / `.fields` for owner-scoped ones.
|
|
29
|
+
*
|
|
30
|
+
* ## Fan-out via wrapped add()
|
|
31
|
+
*
|
|
32
|
+
* `createSemanticModel()` creates a pure SymbolTable, creates the three
|
|
33
|
+
* registries, builds a dispatch table via `createRegistrationTable`, and
|
|
34
|
+
* exposes a SymbolTable-shaped façade whose `add()`:
|
|
35
|
+
*
|
|
36
|
+
* 1. Calls `rawSymbols.add()` — writes the fileIndex + callable index
|
|
37
|
+
* and returns the fully-built `SymbolDefinition`.
|
|
38
|
+
* 2. Runs pre-dispatch normalization (`Function`-with-`ownerId` routes
|
|
39
|
+
* as `Method`).
|
|
40
|
+
* 3. Looks up the dispatch table and invokes the hook, which writes to
|
|
41
|
+
* the appropriate owner-scoped registry.
|
|
42
|
+
*
|
|
43
|
+
* The wrapper is the only place where the two layers are combined. A
|
|
44
|
+
* direct `createSymbolTable()` caller (e.g. an isolated unit test) gets
|
|
45
|
+
* the pure, registry-free behavior — no surprises, no hidden side
|
|
46
|
+
* effects.
|
|
47
|
+
*/
|
|
48
|
+
import type { TypeRegistry, MutableTypeRegistry } from './type-registry.js';
|
|
49
|
+
import type { MethodRegistry, MutableMethodRegistry } from './method-registry.js';
|
|
50
|
+
import type { FieldRegistry, MutableFieldRegistry } from './field-registry.js';
|
|
51
|
+
import type { SymbolTableReader, SymbolTableWriter } from './symbol-table.js';
|
|
52
|
+
/**
|
|
53
|
+
* Aggregated read-only view of the semantic registries plus the nested
|
|
54
|
+
* file/callable SymbolTable.
|
|
55
|
+
*
|
|
56
|
+
* `symbols` is typed as {@link SymbolTableReader} — consumers can query
|
|
57
|
+
* symbols but cannot register new ones or trigger a reset. Callers that
|
|
58
|
+
* need to register symbols or reset state must hold a
|
|
59
|
+
* {@link MutableSemanticModel} reference instead, which widens
|
|
60
|
+
* `symbols` back to {@link SymbolTableWriter} and adds `clear()` on the
|
|
61
|
+
* model itself.
|
|
62
|
+
*
|
|
63
|
+
* This segregation is the runtime half of the principle of least
|
|
64
|
+
* authority: a resolver that receives `SemanticModel` physically cannot
|
|
65
|
+
* mutate the index, so it cannot desync the leaf from the owner-scoped
|
|
66
|
+
* registries even accidentally.
|
|
67
|
+
*/
|
|
68
|
+
export interface SemanticModel {
|
|
69
|
+
readonly types: TypeRegistry;
|
|
70
|
+
readonly methods: MethodRegistry;
|
|
71
|
+
readonly fields: FieldRegistry;
|
|
72
|
+
readonly symbols: SymbolTableReader;
|
|
73
|
+
}
|
|
74
|
+
/** Mutable variant — exposes the MutableX registries, a Writer-typed
|
|
75
|
+
* `symbols` facade, and a full-cascade reset. This is the interface
|
|
76
|
+
* held by the lifecycle owner (pipeline, resolution-context); resolvers
|
|
77
|
+
* that only query should hold the narrower {@link SemanticModel}. */
|
|
78
|
+
export interface MutableSemanticModel extends SemanticModel {
|
|
79
|
+
readonly types: MutableTypeRegistry;
|
|
80
|
+
readonly methods: MutableMethodRegistry;
|
|
81
|
+
readonly fields: MutableFieldRegistry;
|
|
82
|
+
readonly symbols: SymbolTableWriter;
|
|
83
|
+
/** Clear all registries AND the nested SymbolTable. */
|
|
84
|
+
clear(): void;
|
|
85
|
+
}
|
|
86
|
+
export declare const createSemanticModel: () => MutableSemanticModel;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Model
|
|
3
|
+
*
|
|
4
|
+
* Top-level orchestrator for all resolution-time data. Owns:
|
|
5
|
+
*
|
|
6
|
+
* - Three owner-scoped registries (types, methods, fields)
|
|
7
|
+
* - A nested SymbolTable (file + callable name indexes) wrapped so
|
|
8
|
+
* that `add()` fans out into the registries via the dispatch table
|
|
9
|
+
*
|
|
10
|
+
* ## Dependency direction
|
|
11
|
+
*
|
|
12
|
+
* gitnexus-shared (NodeLabel) — leaf
|
|
13
|
+
* ↑
|
|
14
|
+
* symbol-table.ts — pure file/callable index
|
|
15
|
+
* ↑
|
|
16
|
+
* model/type-registry / method-registry / field-registry
|
|
17
|
+
* ↑
|
|
18
|
+
* model/registration-table.ts — dispatch table factory
|
|
19
|
+
* ↑
|
|
20
|
+
* model/semantic-model.ts — THIS FILE (orchestrator)
|
|
21
|
+
* ↑
|
|
22
|
+
* resolve.ts, call-processor.ts, resolution-context.ts, ...
|
|
23
|
+
*
|
|
24
|
+
* `symbol-table.ts` is a leaf — it never imports from `./model/`. This
|
|
25
|
+
* file (semantic-model.ts) is the ONLY place where SymbolTable and the
|
|
26
|
+
* owner-scoped registries are composed. Upstream consumers pass around
|
|
27
|
+
* the `SemanticModel` interface and reach into `.symbols` for file-scoped
|
|
28
|
+
* operations or `.types` / `.methods` / `.fields` for owner-scoped ones.
|
|
29
|
+
*
|
|
30
|
+
* ## Fan-out via wrapped add()
|
|
31
|
+
*
|
|
32
|
+
* `createSemanticModel()` creates a pure SymbolTable, creates the three
|
|
33
|
+
* registries, builds a dispatch table via `createRegistrationTable`, and
|
|
34
|
+
* exposes a SymbolTable-shaped façade whose `add()`:
|
|
35
|
+
*
|
|
36
|
+
* 1. Calls `rawSymbols.add()` — writes the fileIndex + callable index
|
|
37
|
+
* and returns the fully-built `SymbolDefinition`.
|
|
38
|
+
* 2. Runs pre-dispatch normalization (`Function`-with-`ownerId` routes
|
|
39
|
+
* as `Method`).
|
|
40
|
+
* 3. Looks up the dispatch table and invokes the hook, which writes to
|
|
41
|
+
* the appropriate owner-scoped registry.
|
|
42
|
+
*
|
|
43
|
+
* The wrapper is the only place where the two layers are combined. A
|
|
44
|
+
* direct `createSymbolTable()` caller (e.g. an isolated unit test) gets
|
|
45
|
+
* the pure, registry-free behavior — no surprises, no hidden side
|
|
46
|
+
* effects.
|
|
47
|
+
*/
|
|
48
|
+
import { createTypeRegistry } from './type-registry.js';
|
|
49
|
+
import { createMethodRegistry } from './method-registry.js';
|
|
50
|
+
import { createFieldRegistry } from './field-registry.js';
|
|
51
|
+
import { createSymbolTable } from './symbol-table.js';
|
|
52
|
+
import { createRegistrationTable } from './registration-table.js';
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Factory
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
//
|
|
57
|
+
// NodeLabel taxonomy drift detection lives in `registration-table.ts` as a
|
|
58
|
+
// pure compile-time check — the `LABEL_BEHAVIOR` map is
|
|
59
|
+
// `Record<NodeLabel, LabelBehavior>` with `as const satisfies`, which proves
|
|
60
|
+
// coverage, uniqueness, and no-extra-keys at build time. No runtime guard
|
|
61
|
+
// is needed because drift is structurally impossible in the source.
|
|
62
|
+
export const createSemanticModel = () => {
|
|
63
|
+
// 1. Create the pure, registry-unaware SymbolTable leaf.
|
|
64
|
+
// rawSymbols is the only handle in the codebase whose type (the
|
|
65
|
+
// internal createSymbolTable return) includes `.clear()`. cascadeClear
|
|
66
|
+
// below reaches it here; no external caller receives this variable.
|
|
67
|
+
const rawSymbols = createSymbolTable();
|
|
68
|
+
// 2. Create the three owner-scoped registries.
|
|
69
|
+
const types = createTypeRegistry();
|
|
70
|
+
const methods = createMethodRegistry();
|
|
71
|
+
const fields = createFieldRegistry();
|
|
72
|
+
// 3. Build the dispatch table, closed over THIS instance's registries.
|
|
73
|
+
const dispatchTable = createRegistrationTable({ types, methods, fields });
|
|
74
|
+
// 4. Wrap rawSymbols so `add()` fans out into the registries via the
|
|
75
|
+
// dispatch table. See module JSDoc for the three-step contract.
|
|
76
|
+
const wrappedAdd = (filePath, name, nodeId, type, metadata) => {
|
|
77
|
+
const def = rawSymbols.add(filePath, name, nodeId, type, metadata);
|
|
78
|
+
// Function-with-ownerId (Python `def` in a class body, Rust trait
|
|
79
|
+
// method, Kotlin companion method) routes as Method. Keeps the
|
|
80
|
+
// dispatch table single-purpose.
|
|
81
|
+
const dispatchKey = type === 'Function' && metadata?.ownerId !== undefined ? 'Method' : type;
|
|
82
|
+
const hook = dispatchTable.get(dispatchKey);
|
|
83
|
+
if (hook) {
|
|
84
|
+
hook(name, def);
|
|
85
|
+
}
|
|
86
|
+
return def;
|
|
87
|
+
};
|
|
88
|
+
// Cascade clear: single source of truth for "reset the entire model".
|
|
89
|
+
// Wired into both `model.clear()` AND `model.symbols.clear()` so that a
|
|
90
|
+
// caller holding only a SymbolTable reference can't leave the
|
|
91
|
+
// owner-scoped registries populated while the file/callable indexes go
|
|
92
|
+
// empty (the phantom-resolution failure mode).
|
|
93
|
+
const cascadeClear = () => {
|
|
94
|
+
types.clear();
|
|
95
|
+
methods.clear();
|
|
96
|
+
fields.clear();
|
|
97
|
+
rawSymbols.clear();
|
|
98
|
+
};
|
|
99
|
+
// Writer-typed facade: exposes reads + add, but NO `clear` field.
|
|
100
|
+
// Callers holding a `SemanticModel.symbols` reference cannot desync
|
|
101
|
+
// the leaf indexes from the owner-scoped registries. Consumers that
|
|
102
|
+
// only query should widen their annotation to SymbolTableReader for
|
|
103
|
+
// least-authority clarity.
|
|
104
|
+
const symbols = {
|
|
105
|
+
add: wrappedAdd,
|
|
106
|
+
lookupExact: rawSymbols.lookupExact,
|
|
107
|
+
lookupExactFull: rawSymbols.lookupExactFull,
|
|
108
|
+
lookupExactAll: rawSymbols.lookupExactAll,
|
|
109
|
+
lookupCallableByName: rawSymbols.lookupCallableByName,
|
|
110
|
+
getFiles: rawSymbols.getFiles,
|
|
111
|
+
getStats: rawSymbols.getStats,
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
types,
|
|
115
|
+
methods,
|
|
116
|
+
fields,
|
|
117
|
+
symbols,
|
|
118
|
+
clear: cascadeClear,
|
|
119
|
+
};
|
|
120
|
+
};
|