@grafema/core 0.1.0-alpha.5 → 0.1.1-alpha
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 +0 -1
- package/dist/Orchestrator.d.ts +24 -2
- package/dist/Orchestrator.d.ts.map +1 -1
- package/dist/Orchestrator.js +197 -24
- package/dist/config/ConfigLoader.d.ts +72 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +187 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/core/ASTWorker.d.ts +11 -36
- package/dist/core/ASTWorker.d.ts.map +1 -1
- package/dist/core/ASTWorker.js +93 -99
- package/dist/core/CoverageAnalyzer.d.ts +65 -0
- package/dist/core/CoverageAnalyzer.d.ts.map +1 -0
- package/dist/core/CoverageAnalyzer.js +198 -0
- package/dist/core/FileNodeManager.d.ts +40 -0
- package/dist/core/FileNodeManager.d.ts.map +1 -0
- package/dist/core/FileNodeManager.js +84 -0
- package/dist/core/GraphFreshnessChecker.d.ts +33 -0
- package/dist/core/GraphFreshnessChecker.d.ts.map +1 -0
- package/dist/core/GraphFreshnessChecker.js +101 -0
- package/dist/core/HashUtils.d.ts +24 -0
- package/dist/core/HashUtils.d.ts.map +1 -0
- package/dist/core/HashUtils.js +45 -0
- package/dist/core/IncrementalReanalyzer.d.ts +36 -0
- package/dist/core/IncrementalReanalyzer.d.ts.map +1 -0
- package/dist/core/IncrementalReanalyzer.js +132 -0
- package/dist/core/NodeFactory.d.ts +225 -17
- package/dist/core/NodeFactory.d.ts.map +1 -1
- package/dist/core/NodeFactory.js +208 -18
- package/dist/core/ScopeTracker.d.ts +84 -0
- package/dist/core/ScopeTracker.d.ts.map +1 -0
- package/dist/core/ScopeTracker.js +116 -0
- package/dist/core/SemanticId.d.ts +90 -0
- package/dist/core/SemanticId.d.ts.map +1 -0
- package/dist/core/SemanticId.js +115 -0
- package/dist/core/VersionManager.d.ts.map +1 -1
- package/dist/core/VersionManager.js +3 -2
- package/dist/core/nodes/ArgumentExpressionNode.d.ts +43 -0
- package/dist/core/nodes/ArgumentExpressionNode.d.ts.map +1 -0
- package/dist/core/nodes/ArgumentExpressionNode.js +60 -0
- package/dist/core/nodes/ArrayLiteralNode.d.ts +27 -0
- package/dist/core/nodes/ArrayLiteralNode.d.ts.map +1 -0
- package/dist/core/nodes/ArrayLiteralNode.js +41 -0
- package/dist/core/nodes/CallSiteNode.d.ts +28 -0
- package/dist/core/nodes/CallSiteNode.d.ts.map +1 -1
- package/dist/core/nodes/CallSiteNode.js +46 -0
- package/dist/core/nodes/ClassNode.d.ts +33 -1
- package/dist/core/nodes/ClassNode.d.ts.map +1 -1
- package/dist/core/nodes/ClassNode.js +46 -2
- package/dist/core/nodes/DecoratorNode.d.ts +42 -0
- package/dist/core/nodes/DecoratorNode.d.ts.map +1 -0
- package/dist/core/nodes/DecoratorNode.js +62 -0
- package/dist/core/nodes/EnumNode.d.ts +42 -0
- package/dist/core/nodes/EnumNode.d.ts.map +1 -0
- package/dist/core/nodes/EnumNode.js +54 -0
- package/dist/core/nodes/ExportNode.d.ts +37 -1
- package/dist/core/nodes/ExportNode.d.ts.map +1 -1
- package/dist/core/nodes/ExportNode.js +48 -2
- package/dist/core/nodes/ExpressionNode.d.ts +97 -0
- package/dist/core/nodes/ExpressionNode.d.ts.map +1 -0
- package/dist/core/nodes/ExpressionNode.js +178 -0
- package/dist/core/nodes/ExternalModuleNode.d.ts +28 -0
- package/dist/core/nodes/ExternalModuleNode.d.ts.map +1 -0
- package/dist/core/nodes/ExternalModuleNode.js +41 -0
- package/dist/core/nodes/ExternalStdioNode.d.ts +13 -6
- package/dist/core/nodes/ExternalStdioNode.d.ts.map +1 -1
- package/dist/core/nodes/ExternalStdioNode.js +15 -8
- package/dist/core/nodes/FunctionNode.d.ts +36 -0
- package/dist/core/nodes/FunctionNode.d.ts.map +1 -1
- package/dist/core/nodes/FunctionNode.js +80 -1
- package/dist/core/nodes/ImportNode.d.ts +19 -5
- package/dist/core/nodes/ImportNode.d.ts.map +1 -1
- package/dist/core/nodes/ImportNode.js +23 -5
- package/dist/core/nodes/InterfaceNode.d.ts +46 -0
- package/dist/core/nodes/InterfaceNode.d.ts.map +1 -0
- package/dist/core/nodes/InterfaceNode.js +55 -0
- package/dist/core/nodes/IssueNode.d.ts +73 -0
- package/dist/core/nodes/IssueNode.d.ts.map +1 -0
- package/dist/core/nodes/IssueNode.js +129 -0
- package/dist/core/nodes/MethodCallNode.d.ts +30 -0
- package/dist/core/nodes/MethodCallNode.d.ts.map +1 -1
- package/dist/core/nodes/MethodCallNode.js +49 -0
- package/dist/core/nodes/MethodNode.d.ts +32 -0
- package/dist/core/nodes/MethodNode.d.ts.map +1 -1
- package/dist/core/nodes/MethodNode.js +48 -0
- package/dist/core/nodes/ModuleNode.d.ts +31 -0
- package/dist/core/nodes/ModuleNode.d.ts.map +1 -1
- package/dist/core/nodes/ModuleNode.js +37 -0
- package/dist/core/nodes/NetworkRequestNode.d.ts +54 -0
- package/dist/core/nodes/NetworkRequestNode.d.ts.map +1 -0
- package/dist/core/nodes/NetworkRequestNode.js +65 -0
- package/dist/core/nodes/ObjectLiteralNode.d.ts +27 -0
- package/dist/core/nodes/ObjectLiteralNode.d.ts.map +1 -0
- package/dist/core/nodes/ObjectLiteralNode.js +41 -0
- package/dist/core/nodes/ScopeNode.d.ts +31 -0
- package/dist/core/nodes/ScopeNode.d.ts.map +1 -1
- package/dist/core/nodes/ScopeNode.js +49 -0
- package/dist/core/nodes/TypeNode.d.ts +36 -0
- package/dist/core/nodes/TypeNode.d.ts.map +1 -0
- package/dist/core/nodes/TypeNode.js +53 -0
- package/dist/core/nodes/VariableDeclarationNode.d.ts +27 -0
- package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -1
- package/dist/core/nodes/VariableDeclarationNode.js +40 -0
- package/dist/core/nodes/index.d.ts +12 -1
- package/dist/core/nodes/index.d.ts.map +1 -1
- package/dist/core/nodes/index.js +14 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts +98 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticCollector.js +129 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts +77 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticReporter.js +159 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts +31 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticWriter.js +43 -0
- package/dist/diagnostics/index.d.ts +14 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +11 -0
- package/dist/errors/GrafemaError.d.ts +118 -0
- package/dist/errors/GrafemaError.d.ts.map +1 -0
- package/dist/errors/GrafemaError.js +131 -0
- package/dist/index.d.ts +57 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -1
- package/dist/logging/Logger.d.ts +48 -0
- package/dist/logging/Logger.d.ts.map +1 -0
- package/dist/logging/Logger.js +134 -0
- package/dist/plugins/Plugin.d.ts +5 -1
- package/dist/plugins/Plugin.d.ts.map +1 -1
- package/dist/plugins/Plugin.js +33 -0
- package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/DatabaseAnalyzer.js +13 -6
- package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressAnalyzer.js +27 -19
- package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressRouteAnalyzer.js +21 -14
- package/dist/plugins/analysis/FetchAnalyzer.d.ts +1 -0
- package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/FetchAnalyzer.js +34 -14
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts +6 -3
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -1
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +76 -80
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts +180 -17
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/JSASTAnalyzer.js +1171 -471
- package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ReactAnalyzer.js +56 -57
- package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/RustAnalyzer.js +15 -10
- package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SQLiteAnalyzer.js +9 -7
- package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ServiceLayerAnalyzer.js +21 -9
- package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SocketIOAnalyzer.js +27 -15
- package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SystemDbAnalyzer.js +15 -5
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts +34 -4
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/GraphBuilder.js +318 -298
- package/dist/plugins/analysis/ast/IdGenerator.d.ts +105 -0
- package/dist/plugins/analysis/ast/IdGenerator.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/IdGenerator.js +116 -0
- package/dist/plugins/analysis/ast/types.d.ts +176 -5
- package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts +33 -0
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/createParameterNodes.js +89 -0
- package/dist/plugins/analysis/ast/utils/index.d.ts +6 -0
- package/dist/plugins/analysis/ast/utils/index.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/index.js +5 -0
- package/dist/plugins/analysis/ast/utils/location.d.ts +87 -0
- package/dist/plugins/analysis/ast/utils/location.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/location.js +78 -0
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +9 -4
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.js +6 -5
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +99 -9
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +663 -125
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts +4 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +72 -32
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +4 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +128 -63
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +11 -8
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +12 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +36 -14
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +4 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +17 -13
- package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/MonorepoServiceDiscovery.js +3 -2
- package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/SimpleProjectDiscovery.js +5 -1
- package/dist/plugins/discovery/WorkspaceDiscovery.d.ts +22 -0
- package/dist/plugins/discovery/WorkspaceDiscovery.d.ts.map +1 -0
- package/dist/plugins/discovery/WorkspaceDiscovery.js +136 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.d.ts +46 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.d.ts.map +1 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.js +86 -0
- package/dist/plugins/discovery/workspaces/detector.d.ts +21 -0
- package/dist/plugins/discovery/workspaces/detector.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/detector.js +49 -0
- package/dist/plugins/discovery/workspaces/globResolver.d.ts +35 -0
- package/dist/plugins/discovery/workspaces/globResolver.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/globResolver.js +184 -0
- package/dist/plugins/discovery/workspaces/index.d.ts +9 -0
- package/dist/plugins/discovery/workspaces/index.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/index.js +8 -0
- package/dist/plugins/discovery/workspaces/parsers.d.ts +38 -0
- package/dist/plugins/discovery/workspaces/parsers.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/parsers.js +80 -0
- package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -1
- package/dist/plugins/enrichment/AliasTracker.js +14 -8
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/HTTPConnectionEnricher.js +14 -7
- package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -1
- package/dist/plugins/enrichment/ImportExportLinker.js +23 -6
- package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MethodCallResolver.js +18 -12
- package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MountPointResolver.js +8 -3
- package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -1
- package/dist/plugins/enrichment/PrefixEvaluator.js +16 -7
- package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/RustFFIEnricher.js +6 -5
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +17 -0
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js +129 -10
- package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/IncrementalModuleIndexer.js +23 -14
- package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/JSModuleIndexer.js +63 -31
- package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/RustModuleIndexer.js +5 -4
- package/dist/plugins/indexing/ServiceDetector.d.ts +10 -0
- package/dist/plugins/indexing/ServiceDetector.d.ts.map +1 -1
- package/dist/plugins/indexing/ServiceDetector.js +28 -15
- package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -1
- package/dist/plugins/validation/CallResolverValidator.js +8 -7
- package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -1
- package/dist/plugins/validation/DataFlowValidator.js +17 -12
- package/dist/plugins/validation/EvalBanValidator.d.ts.map +1 -1
- package/dist/plugins/validation/EvalBanValidator.js +17 -16
- package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -1
- package/dist/plugins/validation/GraphConnectivityValidator.js +19 -23
- package/dist/plugins/validation/NodeCreationValidator.d.ts +85 -0
- package/dist/plugins/validation/NodeCreationValidator.d.ts.map +1 -0
- package/dist/plugins/validation/NodeCreationValidator.js +415 -0
- package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -1
- package/dist/plugins/validation/SQLInjectionValidator.js +59 -16
- package/dist/plugins/validation/ShadowingDetector.d.ts.map +1 -1
- package/dist/plugins/validation/ShadowingDetector.js +6 -5
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts.map +1 -1
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.js +12 -11
- package/dist/plugins/vcs/GitPlugin.d.ts.map +1 -1
- package/dist/plugins/vcs/GitPlugin.js +10 -12
- package/dist/plugins/vcs/VCSPlugin.d.ts +3 -2
- package/dist/plugins/vcs/VCSPlugin.d.ts.map +1 -1
- package/dist/plugins/vcs/VCSPlugin.js +5 -5
- package/dist/storage/backends/RFDBServerBackend.d.ts +10 -17
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
- package/dist/storage/backends/RFDBServerBackend.js +31 -10
- package/dist/validation/PathValidator.d.ts +1 -2
- package/dist/validation/PathValidator.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Orchestrator.ts +237 -24
- package/src/config/ConfigLoader.ts +263 -0
- package/src/config/index.ts +5 -0
- package/src/core/ASTWorker.ts +143 -139
- package/src/core/CoverageAnalyzer.ts +243 -0
- package/src/core/FileNodeManager.ts +100 -0
- package/src/core/GraphFreshnessChecker.ts +143 -0
- package/src/core/HashUtils.ts +48 -0
- package/src/core/IncrementalReanalyzer.ts +192 -0
- package/src/core/NodeFactory.ts +401 -18
- package/src/core/ScopeTracker.ts +154 -0
- package/src/core/SemanticId.ts +192 -0
- package/src/core/VersionManager.ts +3 -2
- package/src/core/nodes/ArgumentExpressionNode.ts +89 -0
- package/src/core/nodes/ArrayLiteralNode.ts +65 -0
- package/src/core/nodes/CallSiteNode.ts +58 -0
- package/src/core/nodes/ClassNode.ts +63 -2
- package/src/core/nodes/DecoratorNode.ts +91 -0
- package/src/core/nodes/EnumNode.ts +86 -0
- package/src/core/nodes/ExportNode.ts +70 -2
- package/src/core/nodes/ExpressionNode.ts +231 -0
- package/src/core/nodes/ExternalModuleNode.ts +56 -0
- package/src/core/nodes/ExternalStdioNode.ts +17 -9
- package/src/core/nodes/FunctionNode.ts +101 -1
- package/src/core/nodes/ImportNode.ts +32 -10
- package/src/core/nodes/InterfaceNode.ts +91 -0
- package/src/core/nodes/IssueNode.ts +177 -0
- package/src/core/nodes/MethodCallNode.ts +64 -0
- package/src/core/nodes/MethodNode.ts +63 -0
- package/src/core/nodes/ModuleNode.ts +50 -0
- package/src/core/nodes/NetworkRequestNode.ts +77 -0
- package/src/core/nodes/ObjectLiteralNode.ts +65 -0
- package/src/core/nodes/ScopeNode.ts +65 -0
- package/src/core/nodes/TypeNode.ts +78 -0
- package/src/core/nodes/VariableDeclarationNode.ts +52 -0
- package/src/core/nodes/index.ts +18 -1
- package/src/diagnostics/DiagnosticCollector.ts +163 -0
- package/src/diagnostics/DiagnosticReporter.ts +204 -0
- package/src/diagnostics/DiagnosticWriter.ts +50 -0
- package/src/diagnostics/index.ts +16 -0
- package/src/errors/GrafemaError.ts +174 -0
- package/src/index.ts +148 -1
- package/src/logging/Logger.ts +152 -0
- package/src/plugins/Plugin.ts +42 -0
- package/src/plugins/analysis/DatabaseAnalyzer.ts +14 -8
- package/src/plugins/analysis/ExpressAnalyzer.ts +29 -19
- package/src/plugins/analysis/ExpressRouteAnalyzer.ts +22 -21
- package/src/plugins/analysis/FetchAnalyzer.ts +39 -16
- package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +84 -101
- package/src/plugins/analysis/JSASTAnalyzer.ts +1483 -503
- package/src/plugins/analysis/ReactAnalyzer.ts +57 -57
- package/src/plugins/analysis/RustAnalyzer.ts +15 -10
- package/src/plugins/analysis/SQLiteAnalyzer.ts +10 -7
- package/src/plugins/analysis/ServiceLayerAnalyzer.ts +22 -16
- package/src/plugins/analysis/SocketIOAnalyzer.ts +31 -22
- package/src/plugins/analysis/SystemDbAnalyzer.ts +16 -11
- package/src/plugins/analysis/ast/GraphBuilder.ts +439 -327
- package/src/plugins/analysis/ast/IdGenerator.ts +177 -0
- package/src/plugins/analysis/ast/types.ts +209 -6
- package/src/plugins/analysis/ast/utils/createParameterNodes.ts +104 -0
- package/src/plugins/analysis/ast/utils/index.ts +12 -0
- package/src/plugins/analysis/ast/utils/location.ts +103 -0
- package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +11 -8
- package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +909 -83
- package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +97 -44
- package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +159 -93
- package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +12 -8
- package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +41 -14
- package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +37 -17
- package/src/plugins/discovery/MonorepoServiceDiscovery.ts +3 -2
- package/src/plugins/discovery/SimpleProjectDiscovery.ts +6 -1
- package/src/plugins/discovery/WorkspaceDiscovery.ts +177 -0
- package/src/plugins/discovery/resolveSourceEntrypoint.ts +103 -0
- package/src/plugins/discovery/workspaces/detector.ts +63 -0
- package/src/plugins/discovery/workspaces/globResolver.ts +229 -0
- package/src/plugins/discovery/workspaces/index.ts +23 -0
- package/src/plugins/discovery/workspaces/parsers.ts +99 -0
- package/src/plugins/enrichment/AliasTracker.ts +14 -8
- package/src/plugins/enrichment/HTTPConnectionEnricher.ts +14 -7
- package/src/plugins/enrichment/ImportExportLinker.ts +24 -6
- package/src/plugins/enrichment/MethodCallResolver.ts +18 -12
- package/src/plugins/enrichment/MountPointResolver.ts +8 -3
- package/src/plugins/enrichment/PrefixEvaluator.ts +16 -7
- package/src/plugins/enrichment/RustFFIEnricher.ts +6 -5
- package/src/plugins/enrichment/ValueDomainAnalyzer.ts +149 -12
- package/src/plugins/indexing/IncrementalModuleIndexer.ts +23 -14
- package/src/plugins/indexing/JSModuleIndexer.ts +74 -34
- package/src/plugins/indexing/RustModuleIndexer.ts +5 -4
- package/src/plugins/validation/CallResolverValidator.ts +8 -7
- package/src/plugins/validation/DataFlowValidator.ts +16 -12
- package/src/plugins/validation/EvalBanValidator.ts +17 -16
- package/src/plugins/validation/GraphConnectivityValidator.ts +19 -23
- package/src/plugins/validation/NodeCreationValidator.ts +554 -0
- package/src/plugins/validation/SQLInjectionValidator.ts +61 -15
- package/src/plugins/validation/ShadowingDetector.ts +6 -5
- package/src/plugins/validation/TypeScriptDeadCodeValidator.ts +12 -11
- package/src/plugins/vcs/GitPlugin.ts +40 -12
- package/src/plugins/vcs/VCSPlugin.ts +7 -5
- package/src/storage/backends/RFDBServerBackend.ts +43 -29
- package/src/validation/PathValidator.ts +1 -1
- package/dist/core/AnalysisWorker.d.ts +0 -14
- package/dist/core/AnalysisWorker.d.ts.map +0 -1
- package/dist/core/AnalysisWorker.js +0 -307
- package/dist/core/ParallelAnalyzer.d.ts +0 -120
- package/dist/core/ParallelAnalyzer.d.ts.map +0 -1
- package/dist/core/ParallelAnalyzer.js +0 -331
- package/dist/core/QueueWorker.d.ts +0 -12
- package/dist/core/QueueWorker.d.ts.map +0 -1
- package/dist/core/QueueWorker.js +0 -567
- package/dist/core/RFDBClient.d.ts +0 -179
- package/dist/core/RFDBClient.d.ts.map +0 -1
- package/dist/core/RFDBClient.js +0 -429
- package/dist/plugins/discovery/ZonServiceDiscovery.d.ts +0 -19
- package/dist/plugins/discovery/ZonServiceDiscovery.d.ts.map +0 -1
- package/dist/plugins/discovery/ZonServiceDiscovery.js +0 -204
- package/src/core/AnalysisWorker.ts +0 -410
- package/src/core/ParallelAnalyzer.ts +0 -476
- package/src/core/QueueWorker.ts +0 -780
- package/src/plugins/indexing/ServiceDetector.ts +0 -230
|
@@ -8,10 +8,83 @@
|
|
|
8
8
|
* - Constructor calls: new Foo(), new Function()
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { Node, CallExpression, NewExpression, Identifier, MemberExpression } from '@babel/types';
|
|
11
|
+
import type { Node, CallExpression, NewExpression, Identifier, MemberExpression, ObjectExpression, ArrayExpression, ObjectProperty, SpreadElement } from '@babel/types';
|
|
12
12
|
import type { NodePath } from '@babel/traverse';
|
|
13
13
|
import { ASTVisitor, type VisitorModule, type VisitorCollections, type VisitorHandlers, type CounterRef } from './ASTVisitor.js';
|
|
14
14
|
import { ExpressionEvaluator } from '../ExpressionEvaluator.js';
|
|
15
|
+
import type { ArrayMutationInfo, ArrayMutationArgument, ObjectMutationInfo, ObjectMutationValue } from '../types.js';
|
|
16
|
+
import { ScopeTracker } from '../../../../core/ScopeTracker.js';
|
|
17
|
+
import { computeSemanticId } from '../../../../core/SemanticId.js';
|
|
18
|
+
import { IdGenerator } from '../IdGenerator.js';
|
|
19
|
+
import { NodeFactory } from '../../../../core/NodeFactory.js';
|
|
20
|
+
import { ObjectLiteralNode } from '../../../../core/nodes/ObjectLiteralNode.js';
|
|
21
|
+
import { ArrayLiteralNode } from '../../../../core/nodes/ArrayLiteralNode.js';
|
|
22
|
+
import { getLine, getColumn } from '../utils/location.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Object literal info for OBJECT_LITERAL nodes
|
|
26
|
+
*/
|
|
27
|
+
interface ObjectLiteralInfo {
|
|
28
|
+
id: string;
|
|
29
|
+
type: 'OBJECT_LITERAL';
|
|
30
|
+
file: string;
|
|
31
|
+
line: number;
|
|
32
|
+
column: number;
|
|
33
|
+
parentCallId?: string;
|
|
34
|
+
argIndex?: number;
|
|
35
|
+
isSpread?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Object property info for HAS_PROPERTY edges
|
|
40
|
+
*/
|
|
41
|
+
interface ObjectPropertyInfo {
|
|
42
|
+
objectId: string;
|
|
43
|
+
propertyName: string;
|
|
44
|
+
valueNodeId?: string;
|
|
45
|
+
valueType: 'LITERAL' | 'VARIABLE' | 'CALL' | 'EXPRESSION' | 'OBJECT_LITERAL' | 'ARRAY_LITERAL' | 'SPREAD';
|
|
46
|
+
valueName?: string;
|
|
47
|
+
literalValue?: unknown;
|
|
48
|
+
file: string;
|
|
49
|
+
line: number;
|
|
50
|
+
column: number;
|
|
51
|
+
callLine?: number;
|
|
52
|
+
callColumn?: number;
|
|
53
|
+
nestedObjectId?: string;
|
|
54
|
+
nestedArrayId?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Array literal info for ARRAY_LITERAL nodes
|
|
59
|
+
*/
|
|
60
|
+
interface ArrayLiteralInfo {
|
|
61
|
+
id: string;
|
|
62
|
+
type: 'ARRAY_LITERAL';
|
|
63
|
+
file: string;
|
|
64
|
+
line: number;
|
|
65
|
+
column: number;
|
|
66
|
+
parentCallId?: string;
|
|
67
|
+
argIndex?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Array element info for HAS_ELEMENT edges
|
|
72
|
+
*/
|
|
73
|
+
interface ArrayElementInfo {
|
|
74
|
+
arrayId: string;
|
|
75
|
+
index: number;
|
|
76
|
+
valueNodeId?: string;
|
|
77
|
+
valueType: 'LITERAL' | 'VARIABLE' | 'CALL' | 'EXPRESSION' | 'OBJECT_LITERAL' | 'ARRAY_LITERAL' | 'SPREAD';
|
|
78
|
+
valueName?: string;
|
|
79
|
+
literalValue?: unknown;
|
|
80
|
+
file: string;
|
|
81
|
+
line: number;
|
|
82
|
+
column: number;
|
|
83
|
+
callLine?: number;
|
|
84
|
+
callColumn?: number;
|
|
85
|
+
nestedObjectId?: string;
|
|
86
|
+
nestedArrayId?: string;
|
|
87
|
+
}
|
|
15
88
|
|
|
16
89
|
/**
|
|
17
90
|
* Argument info for PASSES_ARGUMENT edges
|
|
@@ -112,8 +185,16 @@ interface LiteralInfo {
|
|
|
112
185
|
}
|
|
113
186
|
|
|
114
187
|
export class CallExpressionVisitor extends ASTVisitor {
|
|
115
|
-
|
|
188
|
+
private scopeTracker?: ScopeTracker;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param module - Current module being analyzed
|
|
192
|
+
* @param collections - Must contain arrays and counter refs
|
|
193
|
+
* @param scopeTracker - Optional ScopeTracker for semantic ID generation
|
|
194
|
+
*/
|
|
195
|
+
constructor(module: VisitorModule, collections: VisitorCollections, scopeTracker?: ScopeTracker) {
|
|
116
196
|
super(module, collections);
|
|
197
|
+
this.scopeTracker = scopeTracker;
|
|
117
198
|
}
|
|
118
199
|
|
|
119
200
|
/**
|
|
@@ -143,27 +224,130 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
143
224
|
actualArg = arg.argument; // Get the actual argument
|
|
144
225
|
}
|
|
145
226
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
227
|
+
// Object literal - check BEFORE extractLiteralValue to handle object-typed args properly
|
|
228
|
+
if (actualArg.type === 'ObjectExpression') {
|
|
229
|
+
const objectExpr = actualArg as ObjectExpression;
|
|
230
|
+
// Initialize collections if not exist (must assign back to this.collections!)
|
|
231
|
+
if (!this.collections.objectLiteralCounterRef) {
|
|
232
|
+
this.collections.objectLiteralCounterRef = { value: 0 };
|
|
233
|
+
}
|
|
234
|
+
if (!this.collections.objectLiterals) {
|
|
235
|
+
this.collections.objectLiterals = [];
|
|
236
|
+
}
|
|
237
|
+
if (!this.collections.objectProperties) {
|
|
238
|
+
this.collections.objectProperties = [];
|
|
239
|
+
}
|
|
240
|
+
const objectLiteralCounterRef = this.collections.objectLiteralCounterRef as CounterRef;
|
|
241
|
+
|
|
242
|
+
// Use factory to create OBJECT_LITERAL node
|
|
243
|
+
const objectNode = ObjectLiteralNode.create(
|
|
244
|
+
module.file,
|
|
245
|
+
argInfo.line,
|
|
246
|
+
argInfo.column,
|
|
247
|
+
{
|
|
248
|
+
parentCallId: callId,
|
|
249
|
+
argIndex: index,
|
|
250
|
+
counter: objectLiteralCounterRef.value++
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
// Factory guarantees line is set, cast to ObjectLiteralInfo
|
|
254
|
+
(this.collections.objectLiterals as ObjectLiteralInfo[]).push(objectNode as unknown as ObjectLiteralInfo);
|
|
255
|
+
const objectId = objectNode.id;
|
|
256
|
+
|
|
257
|
+
// Extract properties
|
|
258
|
+
this.extractObjectProperties(
|
|
259
|
+
objectExpr,
|
|
260
|
+
objectId,
|
|
261
|
+
module,
|
|
262
|
+
this.collections.objectProperties as ObjectPropertyInfo[],
|
|
263
|
+
this.collections.objectLiterals as ObjectLiteralInfo[],
|
|
264
|
+
objectLiteralCounterRef,
|
|
265
|
+
literals as LiteralInfo[],
|
|
266
|
+
literalCounterRef
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
argInfo.targetType = 'OBJECT_LITERAL';
|
|
270
|
+
argInfo.targetId = objectId;
|
|
271
|
+
}
|
|
272
|
+
// Array literal - check BEFORE extractLiteralValue to handle array-typed args properly
|
|
273
|
+
else if (actualArg.type === 'ArrayExpression') {
|
|
274
|
+
const arrayExpr = actualArg as ArrayExpression;
|
|
275
|
+
// Initialize collections if not exist (must assign back to this.collections!)
|
|
276
|
+
if (!this.collections.arrayLiteralCounterRef) {
|
|
277
|
+
this.collections.arrayLiteralCounterRef = { value: 0 };
|
|
278
|
+
}
|
|
279
|
+
if (!this.collections.arrayLiterals) {
|
|
280
|
+
this.collections.arrayLiterals = [];
|
|
281
|
+
}
|
|
282
|
+
if (!this.collections.arrayElements) {
|
|
283
|
+
this.collections.arrayElements = [];
|
|
284
|
+
}
|
|
285
|
+
if (!this.collections.objectLiteralCounterRef) {
|
|
286
|
+
this.collections.objectLiteralCounterRef = { value: 0 };
|
|
287
|
+
}
|
|
288
|
+
if (!this.collections.objectLiterals) {
|
|
289
|
+
this.collections.objectLiterals = [];
|
|
290
|
+
}
|
|
291
|
+
if (!this.collections.objectProperties) {
|
|
292
|
+
this.collections.objectProperties = [];
|
|
293
|
+
}
|
|
294
|
+
const arrayLiteralCounterRef = this.collections.arrayLiteralCounterRef as CounterRef;
|
|
295
|
+
|
|
296
|
+
// Use factory to create ARRAY_LITERAL node
|
|
297
|
+
const arrayNode = ArrayLiteralNode.create(
|
|
298
|
+
module.file,
|
|
299
|
+
argInfo.line,
|
|
300
|
+
argInfo.column,
|
|
301
|
+
{
|
|
302
|
+
parentCallId: callId,
|
|
303
|
+
argIndex: index,
|
|
304
|
+
counter: arrayLiteralCounterRef.value++
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
// Factory guarantees line is set, cast to ArrayLiteralInfo
|
|
308
|
+
(this.collections.arrayLiterals as ArrayLiteralInfo[]).push(arrayNode as unknown as ArrayLiteralInfo);
|
|
309
|
+
const arrayId = arrayNode.id;
|
|
310
|
+
|
|
311
|
+
// Extract elements
|
|
312
|
+
this.extractArrayElements(
|
|
313
|
+
arrayExpr,
|
|
314
|
+
arrayId,
|
|
315
|
+
module,
|
|
316
|
+
this.collections.arrayElements as ArrayElementInfo[],
|
|
317
|
+
this.collections.arrayLiterals as ArrayLiteralInfo[],
|
|
318
|
+
arrayLiteralCounterRef,
|
|
319
|
+
this.collections.objectLiterals as ObjectLiteralInfo[],
|
|
320
|
+
this.collections.objectLiteralCounterRef as CounterRef,
|
|
321
|
+
this.collections.objectProperties as ObjectPropertyInfo[],
|
|
322
|
+
literals as LiteralInfo[],
|
|
323
|
+
literalCounterRef
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
argInfo.targetType = 'ARRAY_LITERAL';
|
|
327
|
+
argInfo.targetId = arrayId;
|
|
164
328
|
}
|
|
165
|
-
//
|
|
166
|
-
else
|
|
329
|
+
// Literal value (primitives only - objects/arrays handled above)
|
|
330
|
+
else {
|
|
331
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(actualArg);
|
|
332
|
+
if (literalValue !== null) {
|
|
333
|
+
const literalId = `LITERAL#arg${index}#${module.file}#${argInfo.line}:${argInfo.column}:${literalCounterRef.value++}`;
|
|
334
|
+
literals.push({
|
|
335
|
+
id: literalId,
|
|
336
|
+
type: 'LITERAL',
|
|
337
|
+
value: literalValue,
|
|
338
|
+
valueType: typeof literalValue,
|
|
339
|
+
file: module.file,
|
|
340
|
+
line: argInfo.line,
|
|
341
|
+
column: argInfo.column,
|
|
342
|
+
parentCallId: callId,
|
|
343
|
+
argIndex: index
|
|
344
|
+
});
|
|
345
|
+
argInfo.targetType = 'LITERAL';
|
|
346
|
+
argInfo.targetId = literalId;
|
|
347
|
+
argInfo.literalValue = literalValue;
|
|
348
|
+
}
|
|
349
|
+
// Variable reference
|
|
350
|
+
else if (actualArg.type === 'Identifier') {
|
|
167
351
|
argInfo.targetType = 'VARIABLE';
|
|
168
352
|
argInfo.targetName = (actualArg as Identifier).name; // Will be resolved in GraphBuilder
|
|
169
353
|
}
|
|
@@ -196,25 +380,26 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
196
380
|
else if (actualArg.type === 'BinaryExpression' || actualArg.type === 'LogicalExpression') {
|
|
197
381
|
const expr = actualArg as { operator?: string; type: string };
|
|
198
382
|
const operator = expr.operator || '?';
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
383
|
+
const counter = literalCounterRef.value++;
|
|
384
|
+
|
|
385
|
+
// Create EXPRESSION node via NodeFactory
|
|
386
|
+
const expressionNode = NodeFactory.createArgumentExpression(
|
|
387
|
+
actualArg.type,
|
|
388
|
+
module.file,
|
|
389
|
+
argInfo.line,
|
|
390
|
+
argInfo.column,
|
|
391
|
+
{
|
|
392
|
+
parentCallId: callId,
|
|
393
|
+
argIndex: index,
|
|
394
|
+
operator,
|
|
395
|
+
counter
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
literals.push(expressionNode as LiteralInfo);
|
|
215
400
|
|
|
216
401
|
argInfo.targetType = 'EXPRESSION';
|
|
217
|
-
argInfo.targetId =
|
|
402
|
+
argInfo.targetId = expressionNode.id;
|
|
218
403
|
argInfo.expressionType = actualArg.type;
|
|
219
404
|
|
|
220
405
|
// Track DERIVES_FROM edges for identifiers in expression
|
|
@@ -223,7 +408,7 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
223
408
|
if (variableAssignments) {
|
|
224
409
|
for (const identName of identifiers) {
|
|
225
410
|
variableAssignments.push({
|
|
226
|
-
variableId:
|
|
411
|
+
variableId: expressionNode.id,
|
|
227
412
|
sourceId: null,
|
|
228
413
|
sourceName: identName,
|
|
229
414
|
sourceType: 'DERIVES_FROM_VARIABLE',
|
|
@@ -232,19 +417,12 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
232
417
|
}
|
|
233
418
|
}
|
|
234
419
|
}
|
|
235
|
-
//
|
|
236
|
-
else if (actualArg.type === 'ObjectExpression') {
|
|
237
|
-
argInfo.targetType = 'OBJECT_LITERAL';
|
|
238
|
-
}
|
|
239
|
-
// Array literal
|
|
240
|
-
else if (actualArg.type === 'ArrayExpression') {
|
|
241
|
-
argInfo.targetType = 'ARRAY_LITERAL';
|
|
242
|
-
}
|
|
243
|
-
// Other expression types
|
|
420
|
+
// Other expression types (fallback for unhandled expression types)
|
|
244
421
|
else {
|
|
245
422
|
argInfo.targetType = 'EXPRESSION';
|
|
246
423
|
argInfo.expressionType = actualArg.type;
|
|
247
424
|
}
|
|
425
|
+
}
|
|
248
426
|
|
|
249
427
|
callArguments.push(argInfo);
|
|
250
428
|
});
|
|
@@ -298,34 +476,572 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
298
476
|
}
|
|
299
477
|
|
|
300
478
|
/**
|
|
301
|
-
*
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
479
|
+
* Extract object properties and create ObjectPropertyInfo records
|
|
480
|
+
*/
|
|
481
|
+
extractObjectProperties(
|
|
482
|
+
objectExpr: ObjectExpression,
|
|
483
|
+
objectId: string,
|
|
484
|
+
module: VisitorModule,
|
|
485
|
+
objectProperties: ObjectPropertyInfo[],
|
|
486
|
+
objectLiterals: ObjectLiteralInfo[],
|
|
487
|
+
objectLiteralCounterRef: CounterRef,
|
|
488
|
+
literals: LiteralInfo[],
|
|
489
|
+
literalCounterRef: CounterRef
|
|
490
|
+
): void {
|
|
491
|
+
for (const prop of objectExpr.properties) {
|
|
492
|
+
const propLine = prop.loc?.start.line || 0;
|
|
493
|
+
const propColumn = prop.loc?.start.column || 0;
|
|
494
|
+
|
|
495
|
+
// Handle spread properties: { ...other }
|
|
496
|
+
if (prop.type === 'SpreadElement') {
|
|
497
|
+
const spreadArg = prop.argument;
|
|
498
|
+
const propertyInfo: ObjectPropertyInfo = {
|
|
499
|
+
objectId,
|
|
500
|
+
propertyName: '<spread>',
|
|
501
|
+
valueType: 'SPREAD',
|
|
502
|
+
file: module.file,
|
|
503
|
+
line: propLine,
|
|
504
|
+
column: propColumn
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
if (spreadArg.type === 'Identifier') {
|
|
508
|
+
propertyInfo.valueName = spreadArg.name;
|
|
509
|
+
propertyInfo.valueType = 'VARIABLE';
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
objectProperties.push(propertyInfo);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Handle regular properties
|
|
517
|
+
if (prop.type === 'ObjectProperty') {
|
|
518
|
+
const objProp = prop as ObjectProperty;
|
|
519
|
+
let propertyName: string;
|
|
520
|
+
|
|
521
|
+
// Get property name
|
|
522
|
+
if (objProp.key.type === 'Identifier') {
|
|
523
|
+
propertyName = objProp.key.name;
|
|
524
|
+
} else if (objProp.key.type === 'StringLiteral') {
|
|
525
|
+
propertyName = objProp.key.value;
|
|
526
|
+
} else if (objProp.key.type === 'NumericLiteral') {
|
|
527
|
+
propertyName = String(objProp.key.value);
|
|
528
|
+
} else {
|
|
529
|
+
propertyName = '<computed>';
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const propertyInfo: ObjectPropertyInfo = {
|
|
533
|
+
objectId,
|
|
534
|
+
propertyName,
|
|
535
|
+
file: module.file,
|
|
536
|
+
line: propLine,
|
|
537
|
+
column: propColumn,
|
|
538
|
+
valueType: 'EXPRESSION'
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const value = objProp.value;
|
|
542
|
+
|
|
543
|
+
// Nested object literal - check BEFORE extractLiteralValue
|
|
544
|
+
if (value.type === 'ObjectExpression') {
|
|
545
|
+
// Use factory - do NOT pass argIndex for nested literals (uses 'obj' suffix)
|
|
546
|
+
const nestedObjectNode = ObjectLiteralNode.create(
|
|
547
|
+
module.file,
|
|
548
|
+
value.loc?.start.line || 0,
|
|
549
|
+
value.loc?.start.column || 0,
|
|
550
|
+
{
|
|
551
|
+
counter: objectLiteralCounterRef.value++
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
objectLiterals.push(nestedObjectNode as unknown as ObjectLiteralInfo);
|
|
555
|
+
const nestedObjectId = nestedObjectNode.id;
|
|
556
|
+
|
|
557
|
+
// Recursively extract nested properties
|
|
558
|
+
this.extractObjectProperties(
|
|
559
|
+
value,
|
|
560
|
+
nestedObjectId,
|
|
561
|
+
module,
|
|
562
|
+
objectProperties,
|
|
563
|
+
objectLiterals,
|
|
564
|
+
objectLiteralCounterRef,
|
|
565
|
+
literals,
|
|
566
|
+
literalCounterRef
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
propertyInfo.valueType = 'OBJECT_LITERAL';
|
|
570
|
+
propertyInfo.nestedObjectId = nestedObjectId;
|
|
571
|
+
propertyInfo.valueNodeId = nestedObjectId;
|
|
572
|
+
}
|
|
573
|
+
// Nested array literal - check BEFORE extractLiteralValue
|
|
574
|
+
else if (value.type === 'ArrayExpression') {
|
|
575
|
+
const arrayLiteralCounterRef = (this.collections.arrayLiteralCounterRef ?? { value: 0 }) as CounterRef;
|
|
576
|
+
const arrayLiterals = this.collections.arrayLiterals ?? [];
|
|
577
|
+
const arrayElements = this.collections.arrayElements ?? [];
|
|
578
|
+
|
|
579
|
+
// Use factory - do NOT pass argIndex for nested literals (uses 'arr' suffix)
|
|
580
|
+
const nestedArrayNode = ArrayLiteralNode.create(
|
|
581
|
+
module.file,
|
|
582
|
+
value.loc?.start.line || 0,
|
|
583
|
+
value.loc?.start.column || 0,
|
|
584
|
+
{
|
|
585
|
+
counter: arrayLiteralCounterRef.value++
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
(arrayLiterals as ArrayLiteralInfo[]).push(nestedArrayNode as unknown as ArrayLiteralInfo);
|
|
589
|
+
const nestedArrayId = nestedArrayNode.id;
|
|
590
|
+
|
|
591
|
+
// Recursively extract array elements
|
|
592
|
+
this.extractArrayElements(
|
|
593
|
+
value,
|
|
594
|
+
nestedArrayId,
|
|
595
|
+
module,
|
|
596
|
+
arrayElements as ArrayElementInfo[],
|
|
597
|
+
arrayLiterals as ArrayLiteralInfo[],
|
|
598
|
+
arrayLiteralCounterRef,
|
|
599
|
+
objectLiterals,
|
|
600
|
+
objectLiteralCounterRef,
|
|
601
|
+
objectProperties,
|
|
602
|
+
literals,
|
|
603
|
+
literalCounterRef
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
propertyInfo.valueType = 'ARRAY_LITERAL';
|
|
607
|
+
propertyInfo.nestedArrayId = nestedArrayId;
|
|
608
|
+
propertyInfo.valueNodeId = nestedArrayId;
|
|
609
|
+
}
|
|
610
|
+
// Literal value (primitives only - objects/arrays handled above)
|
|
611
|
+
else {
|
|
612
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(value);
|
|
613
|
+
if (literalValue !== null) {
|
|
614
|
+
const literalId = `LITERAL#${propertyName}#${module.file}#${propLine}:${propColumn}:${literalCounterRef.value++}`;
|
|
615
|
+
literals.push({
|
|
616
|
+
id: literalId,
|
|
617
|
+
type: 'LITERAL',
|
|
618
|
+
value: literalValue,
|
|
619
|
+
valueType: typeof literalValue,
|
|
620
|
+
file: module.file,
|
|
621
|
+
line: propLine,
|
|
622
|
+
column: propColumn,
|
|
623
|
+
parentCallId: objectId,
|
|
624
|
+
argIndex: 0
|
|
625
|
+
});
|
|
626
|
+
propertyInfo.valueType = 'LITERAL';
|
|
627
|
+
propertyInfo.valueNodeId = literalId;
|
|
628
|
+
propertyInfo.literalValue = literalValue;
|
|
629
|
+
}
|
|
630
|
+
// Variable reference
|
|
631
|
+
else if (value.type === 'Identifier') {
|
|
632
|
+
propertyInfo.valueType = 'VARIABLE';
|
|
633
|
+
propertyInfo.valueName = value.name;
|
|
634
|
+
}
|
|
635
|
+
// Call expression
|
|
636
|
+
else if (value.type === 'CallExpression') {
|
|
637
|
+
propertyInfo.valueType = 'CALL';
|
|
638
|
+
propertyInfo.callLine = value.loc?.start.line;
|
|
639
|
+
propertyInfo.callColumn = value.loc?.start.column;
|
|
640
|
+
}
|
|
641
|
+
// Other expressions
|
|
642
|
+
else {
|
|
643
|
+
propertyInfo.valueType = 'EXPRESSION';
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
objectProperties.push(propertyInfo);
|
|
648
|
+
}
|
|
649
|
+
// Handle object methods: { foo() {} }
|
|
650
|
+
else if (prop.type === 'ObjectMethod') {
|
|
651
|
+
const propertyName = prop.key.type === 'Identifier' ? prop.key.name : '<computed>';
|
|
652
|
+
objectProperties.push({
|
|
653
|
+
objectId,
|
|
654
|
+
propertyName,
|
|
655
|
+
valueType: 'EXPRESSION',
|
|
656
|
+
file: module.file,
|
|
657
|
+
line: propLine,
|
|
658
|
+
column: propColumn
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Extract array elements and create ArrayElementInfo records
|
|
666
|
+
*/
|
|
667
|
+
extractArrayElements(
|
|
668
|
+
arrayExpr: ArrayExpression,
|
|
669
|
+
arrayId: string,
|
|
670
|
+
module: VisitorModule,
|
|
671
|
+
arrayElements: ArrayElementInfo[],
|
|
672
|
+
arrayLiterals: ArrayLiteralInfo[],
|
|
673
|
+
arrayLiteralCounterRef: CounterRef,
|
|
674
|
+
objectLiterals: ObjectLiteralInfo[],
|
|
675
|
+
objectLiteralCounterRef: CounterRef,
|
|
676
|
+
objectProperties: ObjectPropertyInfo[],
|
|
677
|
+
literals: LiteralInfo[],
|
|
678
|
+
literalCounterRef: CounterRef
|
|
679
|
+
): void {
|
|
680
|
+
arrayExpr.elements.forEach((element, index) => {
|
|
681
|
+
if (!element) return; // Skip holes in arrays
|
|
682
|
+
|
|
683
|
+
const elemLine = element.loc?.start.line || 0;
|
|
684
|
+
const elemColumn = element.loc?.start.column || 0;
|
|
685
|
+
|
|
686
|
+
const elementInfo: ArrayElementInfo = {
|
|
687
|
+
arrayId,
|
|
688
|
+
index,
|
|
689
|
+
file: module.file,
|
|
690
|
+
line: elemLine,
|
|
691
|
+
column: elemColumn,
|
|
692
|
+
valueType: 'EXPRESSION'
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
// Handle spread elements: [...arr]
|
|
696
|
+
if (element.type === 'SpreadElement') {
|
|
697
|
+
const spreadArg = element.argument;
|
|
698
|
+
elementInfo.valueType = 'SPREAD';
|
|
699
|
+
if (spreadArg.type === 'Identifier') {
|
|
700
|
+
elementInfo.valueName = spreadArg.name;
|
|
701
|
+
}
|
|
702
|
+
arrayElements.push(elementInfo);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Nested object literal - check BEFORE extractLiteralValue
|
|
707
|
+
if (element.type === 'ObjectExpression') {
|
|
708
|
+
// Use factory - do NOT pass argIndex for nested literals (uses 'obj' suffix)
|
|
709
|
+
const nestedObjectNode = ObjectLiteralNode.create(
|
|
710
|
+
module.file,
|
|
711
|
+
elemLine,
|
|
712
|
+
elemColumn,
|
|
713
|
+
{
|
|
714
|
+
counter: objectLiteralCounterRef.value++
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
objectLiterals.push(nestedObjectNode as unknown as ObjectLiteralInfo);
|
|
718
|
+
const nestedObjectId = nestedObjectNode.id;
|
|
719
|
+
|
|
720
|
+
// Recursively extract properties
|
|
721
|
+
this.extractObjectProperties(
|
|
722
|
+
element,
|
|
723
|
+
nestedObjectId,
|
|
724
|
+
module,
|
|
725
|
+
objectProperties,
|
|
726
|
+
objectLiterals,
|
|
727
|
+
objectLiteralCounterRef,
|
|
728
|
+
literals,
|
|
729
|
+
literalCounterRef
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
elementInfo.valueType = 'OBJECT_LITERAL';
|
|
733
|
+
elementInfo.nestedObjectId = nestedObjectId;
|
|
734
|
+
elementInfo.valueNodeId = nestedObjectId;
|
|
735
|
+
}
|
|
736
|
+
// Nested array literal - check BEFORE extractLiteralValue
|
|
737
|
+
else if (element.type === 'ArrayExpression') {
|
|
738
|
+
// Use factory - do NOT pass argIndex for nested literals (uses 'arr' suffix)
|
|
739
|
+
const nestedArrayNode = ArrayLiteralNode.create(
|
|
740
|
+
module.file,
|
|
741
|
+
elemLine,
|
|
742
|
+
elemColumn,
|
|
743
|
+
{
|
|
744
|
+
counter: arrayLiteralCounterRef.value++
|
|
745
|
+
}
|
|
746
|
+
);
|
|
747
|
+
arrayLiterals.push(nestedArrayNode as unknown as ArrayLiteralInfo);
|
|
748
|
+
const nestedArrayId = nestedArrayNode.id;
|
|
749
|
+
|
|
750
|
+
// Recursively extract elements
|
|
751
|
+
this.extractArrayElements(
|
|
752
|
+
element,
|
|
753
|
+
nestedArrayId,
|
|
754
|
+
module,
|
|
755
|
+
arrayElements,
|
|
756
|
+
arrayLiterals,
|
|
757
|
+
arrayLiteralCounterRef,
|
|
758
|
+
objectLiterals,
|
|
759
|
+
objectLiteralCounterRef,
|
|
760
|
+
objectProperties,
|
|
761
|
+
literals,
|
|
762
|
+
literalCounterRef
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
elementInfo.valueType = 'ARRAY_LITERAL';
|
|
766
|
+
elementInfo.nestedArrayId = nestedArrayId;
|
|
767
|
+
elementInfo.valueNodeId = nestedArrayId;
|
|
768
|
+
}
|
|
769
|
+
// Literal value (primitives only - objects/arrays handled above)
|
|
770
|
+
else {
|
|
771
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(element);
|
|
772
|
+
if (literalValue !== null) {
|
|
773
|
+
const literalId = `LITERAL#elem${index}#${module.file}#${elemLine}:${elemColumn}:${literalCounterRef.value++}`;
|
|
774
|
+
literals.push({
|
|
775
|
+
id: literalId,
|
|
776
|
+
type: 'LITERAL',
|
|
777
|
+
value: literalValue,
|
|
778
|
+
valueType: typeof literalValue,
|
|
779
|
+
file: module.file,
|
|
780
|
+
line: elemLine,
|
|
781
|
+
column: elemColumn,
|
|
782
|
+
parentCallId: arrayId,
|
|
783
|
+
argIndex: index
|
|
784
|
+
});
|
|
785
|
+
elementInfo.valueType = 'LITERAL';
|
|
786
|
+
elementInfo.valueNodeId = literalId;
|
|
787
|
+
elementInfo.literalValue = literalValue;
|
|
788
|
+
}
|
|
789
|
+
// Variable reference
|
|
790
|
+
else if (element.type === 'Identifier') {
|
|
791
|
+
elementInfo.valueType = 'VARIABLE';
|
|
792
|
+
elementInfo.valueName = element.name;
|
|
793
|
+
}
|
|
794
|
+
// Call expression
|
|
795
|
+
else if (element.type === 'CallExpression') {
|
|
796
|
+
elementInfo.valueType = 'CALL';
|
|
797
|
+
elementInfo.callLine = element.loc?.start.line;
|
|
798
|
+
elementInfo.callColumn = element.loc?.start.column;
|
|
799
|
+
}
|
|
800
|
+
// Other expressions
|
|
801
|
+
else {
|
|
802
|
+
elementInfo.valueType = 'EXPRESSION';
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
arrayElements.push(elementInfo);
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Detect array mutation calls (push, unshift, splice) and collect mutation info
|
|
812
|
+
* for later FLOWS_INTO edge creation in GraphBuilder
|
|
813
|
+
*
|
|
814
|
+
* REG-117: Added isNested, baseObjectName, propertyName for nested mutations
|
|
815
|
+
*/
|
|
816
|
+
private detectArrayMutation(
|
|
817
|
+
callNode: CallExpression,
|
|
818
|
+
arrayName: string,
|
|
819
|
+
method: 'push' | 'unshift' | 'splice',
|
|
820
|
+
module: VisitorModule,
|
|
821
|
+
isNested?: boolean,
|
|
822
|
+
baseObjectName?: string,
|
|
823
|
+
propertyName?: string
|
|
824
|
+
): void {
|
|
825
|
+
// Initialize collection if not exists
|
|
826
|
+
if (!this.collections.arrayMutations) {
|
|
827
|
+
this.collections.arrayMutations = [];
|
|
828
|
+
}
|
|
829
|
+
const arrayMutations = this.collections.arrayMutations as ArrayMutationInfo[];
|
|
830
|
+
|
|
831
|
+
const mutationArgs: ArrayMutationArgument[] = [];
|
|
832
|
+
|
|
833
|
+
// For splice, only arguments from index 2 onwards are insertions
|
|
834
|
+
// splice(start, deleteCount, item1, item2, ...)
|
|
835
|
+
callNode.arguments.forEach((arg, index) => {
|
|
836
|
+
// Skip start and deleteCount for splice
|
|
837
|
+
if (method === 'splice' && index < 2) return;
|
|
838
|
+
|
|
839
|
+
const argInfo: ArrayMutationArgument = {
|
|
840
|
+
argIndex: method === 'splice' ? index - 2 : index,
|
|
841
|
+
isSpread: arg.type === 'SpreadElement',
|
|
842
|
+
valueType: 'EXPRESSION' // Default
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
let actualArg = arg;
|
|
846
|
+
if (arg.type === 'SpreadElement') {
|
|
847
|
+
actualArg = arg.argument;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Determine value type
|
|
851
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(actualArg);
|
|
852
|
+
if (literalValue !== null) {
|
|
853
|
+
argInfo.valueType = 'LITERAL';
|
|
854
|
+
argInfo.literalValue = literalValue;
|
|
855
|
+
} else if (actualArg.type === 'Identifier') {
|
|
856
|
+
argInfo.valueType = 'VARIABLE';
|
|
857
|
+
argInfo.valueName = actualArg.name;
|
|
858
|
+
} else if (actualArg.type === 'ObjectExpression') {
|
|
859
|
+
argInfo.valueType = 'OBJECT_LITERAL';
|
|
860
|
+
} else if (actualArg.type === 'ArrayExpression') {
|
|
861
|
+
argInfo.valueType = 'ARRAY_LITERAL';
|
|
862
|
+
} else if (actualArg.type === 'CallExpression') {
|
|
863
|
+
argInfo.valueType = 'CALL';
|
|
864
|
+
argInfo.callLine = actualArg.loc?.start.line;
|
|
865
|
+
argInfo.callColumn = actualArg.loc?.start.column;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
mutationArgs.push(argInfo);
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
// Only record if there are actual insertions
|
|
872
|
+
if (mutationArgs.length > 0) {
|
|
873
|
+
const line = callNode.loc?.start.line ?? 0;
|
|
874
|
+
const column = callNode.loc?.start.column ?? 0;
|
|
875
|
+
|
|
876
|
+
// Generate semantic ID for array mutation if scopeTracker available
|
|
877
|
+
const scopeTracker = this.scopeTracker;
|
|
878
|
+
let mutationId: string | undefined;
|
|
879
|
+
if (scopeTracker) {
|
|
880
|
+
const discriminator = scopeTracker.getItemCounter(`ARRAY_MUTATION:${arrayName}.${method}`);
|
|
881
|
+
mutationId = computeSemanticId('ARRAY_MUTATION', `${arrayName}.${method}`, scopeTracker.getContext(), { discriminator });
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
arrayMutations.push({
|
|
885
|
+
id: mutationId,
|
|
886
|
+
arrayName,
|
|
887
|
+
mutationMethod: method,
|
|
888
|
+
file: module.file,
|
|
889
|
+
line,
|
|
890
|
+
column,
|
|
891
|
+
insertedValues: mutationArgs,
|
|
892
|
+
// REG-117: Nested mutation fields
|
|
893
|
+
isNested,
|
|
894
|
+
baseObjectName,
|
|
895
|
+
propertyName
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Detect Object.assign(target, source1, source2, ...) calls
|
|
902
|
+
* Creates ObjectMutationInfo for FLOWS_INTO edge generation in GraphBuilder
|
|
903
|
+
*
|
|
904
|
+
* @param callNode - The call expression node
|
|
905
|
+
* @param module - Current module being analyzed
|
|
906
|
+
*/
|
|
907
|
+
private detectObjectAssign(
|
|
908
|
+
callNode: CallExpression,
|
|
909
|
+
module: VisitorModule
|
|
910
|
+
): void {
|
|
911
|
+
// Need at least 2 arguments: target and at least one source
|
|
912
|
+
if (callNode.arguments.length < 2) return;
|
|
913
|
+
|
|
914
|
+
// Initialize object mutations collection if not exists
|
|
915
|
+
if (!this.collections.objectMutations) {
|
|
916
|
+
this.collections.objectMutations = [];
|
|
917
|
+
}
|
|
918
|
+
const objectMutations = this.collections.objectMutations as ObjectMutationInfo[];
|
|
919
|
+
|
|
920
|
+
// First argument is target
|
|
921
|
+
const targetArg = callNode.arguments[0];
|
|
922
|
+
let targetName: string;
|
|
923
|
+
|
|
924
|
+
if (targetArg.type === 'Identifier') {
|
|
925
|
+
targetName = targetArg.name;
|
|
926
|
+
} else if (targetArg.type === 'ObjectExpression') {
|
|
927
|
+
targetName = '<anonymous>';
|
|
928
|
+
} else {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
const line = callNode.loc?.start.line ?? 0;
|
|
933
|
+
const column = callNode.loc?.start.column ?? 0;
|
|
934
|
+
|
|
935
|
+
for (let i = 1; i < callNode.arguments.length; i++) {
|
|
936
|
+
let arg = callNode.arguments[i];
|
|
937
|
+
let isSpread = false;
|
|
938
|
+
|
|
939
|
+
if (arg.type === 'SpreadElement') {
|
|
940
|
+
isSpread = true;
|
|
941
|
+
arg = arg.argument;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const valueInfo: ObjectMutationValue = {
|
|
945
|
+
valueType: 'EXPRESSION',
|
|
946
|
+
argIndex: i - 1,
|
|
947
|
+
isSpread
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(arg);
|
|
951
|
+
if (literalValue !== null) {
|
|
952
|
+
valueInfo.valueType = 'LITERAL';
|
|
953
|
+
valueInfo.literalValue = literalValue;
|
|
954
|
+
} else if (arg.type === 'Identifier') {
|
|
955
|
+
valueInfo.valueType = 'VARIABLE';
|
|
956
|
+
valueInfo.valueName = arg.name;
|
|
957
|
+
} else if (arg.type === 'ObjectExpression') {
|
|
958
|
+
valueInfo.valueType = 'OBJECT_LITERAL';
|
|
959
|
+
} else if (arg.type === 'ArrayExpression') {
|
|
960
|
+
valueInfo.valueType = 'ARRAY_LITERAL';
|
|
961
|
+
} else if (arg.type === 'CallExpression') {
|
|
962
|
+
valueInfo.valueType = 'CALL';
|
|
963
|
+
valueInfo.callLine = arg.loc?.start.line;
|
|
964
|
+
valueInfo.callColumn = arg.loc?.start.column;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
let mutationId: string | undefined;
|
|
968
|
+
if (this.scopeTracker) {
|
|
969
|
+
const discriminator = this.scopeTracker.getItemCounter(`OBJECT_MUTATION:Object.assign:${targetName}`);
|
|
970
|
+
mutationId = computeSemanticId('OBJECT_MUTATION', `Object.assign:${targetName}`, this.scopeTracker.getContext(), { discriminator });
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
objectMutations.push({
|
|
974
|
+
id: mutationId,
|
|
975
|
+
objectName: targetName,
|
|
976
|
+
propertyName: '<assign>',
|
|
977
|
+
mutationType: 'assign',
|
|
978
|
+
file: module.file,
|
|
979
|
+
line,
|
|
980
|
+
column,
|
|
981
|
+
value: valueInfo
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Get a stable scope ID for a function parent.
|
|
988
|
+
*
|
|
989
|
+
* Format must match what FunctionVisitor/ClassVisitor creates (semantic ID):
|
|
990
|
+
* - Module-level function: {file}->global->FUNCTION->{name}
|
|
991
|
+
* - Class method: {file}->{className}->FUNCTION->{methodName}
|
|
305
992
|
*
|
|
306
|
-
*
|
|
307
|
-
* we try to match by name+file+line:col. This may not always work for
|
|
308
|
-
* multiple arrow functions on the same line.
|
|
993
|
+
* Reconstructs scope path by walking up the AST.
|
|
309
994
|
*/
|
|
310
995
|
getFunctionScopeId(functionParent: NodePath, module: VisitorModule): string {
|
|
311
996
|
const funcNode = functionParent.node as Node & {
|
|
312
997
|
id?: { name: string } | null;
|
|
998
|
+
key?: { name?: string; type: string };
|
|
313
999
|
loc?: { start: { line: number; column: number } };
|
|
314
1000
|
type: string;
|
|
315
1001
|
};
|
|
316
|
-
const line = funcNode.loc?.start.line || 0;
|
|
317
|
-
const col = funcNode.loc?.start.column || 0;
|
|
318
1002
|
|
|
319
|
-
//
|
|
1003
|
+
// Get function name
|
|
1004
|
+
let funcName: string | undefined;
|
|
320
1005
|
if (funcNode.type === 'FunctionDeclaration' && funcNode.id?.name) {
|
|
321
|
-
|
|
1006
|
+
funcName = funcNode.id.name;
|
|
1007
|
+
} else if (funcNode.type === 'ClassMethod' && funcNode.key?.type === 'Identifier') {
|
|
1008
|
+
funcName = funcNode.key.name;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
if (!funcName) {
|
|
1012
|
+
// Anonymous function - fall back to module scope
|
|
1013
|
+
return module.id;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// Build scope path by walking up the AST
|
|
1017
|
+
const scopePath: string[] = [];
|
|
1018
|
+
let current: NodePath | null = functionParent.parentPath;
|
|
1019
|
+
|
|
1020
|
+
while (current) {
|
|
1021
|
+
const node = current.node as Node & {
|
|
1022
|
+
id?: { name: string } | null;
|
|
1023
|
+
type: string;
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
if (node.type === 'ClassDeclaration' && node.id?.name) {
|
|
1027
|
+
scopePath.unshift(node.id.name);
|
|
1028
|
+
break; // Class is the outermost scope we need
|
|
1029
|
+
} else if (node.type === 'ClassBody') {
|
|
1030
|
+
// Continue up to ClassDeclaration
|
|
1031
|
+
} else if (node.type === 'Program') {
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
current = current.parentPath;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// If no class found, it's at module level (global scope)
|
|
1039
|
+
if (scopePath.length === 0) {
|
|
1040
|
+
scopePath.push('global');
|
|
322
1041
|
}
|
|
323
1042
|
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
// to avoid creating invalid edges. The CALL node will be connected to MODULE
|
|
327
|
-
// instead of the specific function.
|
|
328
|
-
return module.id;
|
|
1043
|
+
// Compute semantic ID: {file}->{scopePath}->FUNCTION->{name}
|
|
1044
|
+
return `${module.file}->${scopePath.join('->')}->FUNCTION->${funcName}`;
|
|
329
1045
|
}
|
|
330
1046
|
|
|
331
1047
|
getHandlers(): VisitorHandlers {
|
|
@@ -339,26 +1055,43 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
339
1055
|
const callSiteCounterRef = (this.collections.callSiteCounterRef ?? { value: 0 }) as CounterRef;
|
|
340
1056
|
const literalCounterRef = (this.collections.literalCounterRef ?? { value: 0 }) as CounterRef;
|
|
341
1057
|
const processedNodes = this.collections.processedNodes ?? { callSites: new Set(), methodCalls: new Set(), eventListeners: new Set() };
|
|
1058
|
+
const scopeTracker = this.scopeTracker;
|
|
342
1059
|
|
|
343
1060
|
return {
|
|
344
1061
|
CallExpression: (path: NodePath) => {
|
|
345
1062
|
const callNode = path.node as CallExpression;
|
|
346
1063
|
const functionParent = path.getFunctionParent();
|
|
1064
|
+
|
|
347
1065
|
// Determine parent scope - if inside a function, use function's scope, otherwise module
|
|
348
1066
|
const parentScopeId = functionParent ? this.getFunctionScopeId(functionParent, module) : module.id;
|
|
349
1067
|
|
|
350
1068
|
// Identifier calls (direct function calls)
|
|
1069
|
+
// Skip if inside function - they will be processed by analyzeFunctionBody with proper scope tracking
|
|
351
1070
|
if (callNode.callee.type === 'Identifier') {
|
|
1071
|
+
if (functionParent) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
352
1074
|
const callee = callNode.callee as Identifier;
|
|
353
|
-
|
|
1075
|
+
|
|
1076
|
+
const line = getLine(callNode);
|
|
1077
|
+
const column = getColumn(callNode);
|
|
1078
|
+
|
|
1079
|
+
// Generate ID using centralized IdGenerator
|
|
1080
|
+
const idGenerator = new IdGenerator(scopeTracker);
|
|
1081
|
+
const callId = idGenerator.generate(
|
|
1082
|
+
'CALL', callee.name, module.file,
|
|
1083
|
+
line, column,
|
|
1084
|
+
callSiteCounterRef,
|
|
1085
|
+
{ useDiscriminator: true, discriminatorKey: `CALL:${callee.name}` }
|
|
1086
|
+
);
|
|
354
1087
|
|
|
355
1088
|
(callSites as CallSiteInfo[]).push({
|
|
356
1089
|
id: callId,
|
|
357
1090
|
type: 'CALL',
|
|
358
1091
|
name: callee.name,
|
|
359
1092
|
file: module.file,
|
|
360
|
-
line
|
|
361
|
-
column
|
|
1093
|
+
line,
|
|
1094
|
+
column,
|
|
362
1095
|
parentScopeId,
|
|
363
1096
|
targetFunctionName: callee.name
|
|
364
1097
|
});
|
|
@@ -375,8 +1108,12 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
375
1108
|
);
|
|
376
1109
|
}
|
|
377
1110
|
}
|
|
378
|
-
// MemberExpression calls (method calls
|
|
1111
|
+
// MemberExpression calls (method calls)
|
|
1112
|
+
// Skip if inside function - they will be processed by analyzeFunctionBody with proper scope tracking
|
|
379
1113
|
else if (callNode.callee.type === 'MemberExpression') {
|
|
1114
|
+
if (functionParent) {
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
380
1117
|
const memberCallee = callNode.callee as MemberExpression;
|
|
381
1118
|
const object = memberCallee.object;
|
|
382
1119
|
const property = memberCallee.property;
|
|
@@ -403,13 +1140,16 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
403
1140
|
}
|
|
404
1141
|
processedNodes.eventListeners.add(nodeKey);
|
|
405
1142
|
|
|
1143
|
+
const eventLine = getLine(callNode);
|
|
1144
|
+
const eventColumn = getColumn(callNode);
|
|
1145
|
+
|
|
406
1146
|
(eventListeners as EventListenerInfo[]).push({
|
|
407
|
-
id: `event:listener#${eventName}#${module.file}#${
|
|
1147
|
+
id: `event:listener#${eventName}#${module.file}#${eventLine}:${eventColumn}:${callSiteCounterRef.value++}`,
|
|
408
1148
|
type: 'event:listener',
|
|
409
1149
|
name: eventName,
|
|
410
1150
|
object: objectName,
|
|
411
1151
|
file: module.file,
|
|
412
|
-
line:
|
|
1152
|
+
line: eventLine,
|
|
413
1153
|
parentScopeId,
|
|
414
1154
|
callbackArg: secondArg
|
|
415
1155
|
});
|
|
@@ -423,7 +1163,17 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
423
1163
|
processedNodes.methodCalls.add(nodeKey);
|
|
424
1164
|
|
|
425
1165
|
const fullName = `${objectName}.${methodName}`;
|
|
426
|
-
const
|
|
1166
|
+
const methodLine = getLine(callNode);
|
|
1167
|
+
const methodColumn = getColumn(callNode);
|
|
1168
|
+
|
|
1169
|
+
// Generate ID using centralized IdGenerator
|
|
1170
|
+
const idGenerator = new IdGenerator(scopeTracker);
|
|
1171
|
+
const methodCallId = idGenerator.generate(
|
|
1172
|
+
'CALL', fullName, module.file,
|
|
1173
|
+
methodLine, methodColumn,
|
|
1174
|
+
callSiteCounterRef,
|
|
1175
|
+
{ useDiscriminator: true, discriminatorKey: `CALL:${fullName}` }
|
|
1176
|
+
);
|
|
427
1177
|
|
|
428
1178
|
(methodCalls as MethodCallInfo[]).push({
|
|
429
1179
|
id: methodCallId,
|
|
@@ -434,11 +1184,27 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
434
1184
|
computed: isComputed,
|
|
435
1185
|
computedPropertyVar, // Variable name used in obj[x]() calls
|
|
436
1186
|
file: module.file,
|
|
437
|
-
line:
|
|
438
|
-
column:
|
|
1187
|
+
line: methodLine,
|
|
1188
|
+
column: methodColumn,
|
|
439
1189
|
parentScopeId
|
|
440
1190
|
});
|
|
441
1191
|
|
|
1192
|
+
// Check for array mutation methods (push, unshift, splice)
|
|
1193
|
+
const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
|
|
1194
|
+
if (ARRAY_MUTATION_METHODS.includes(methodName)) {
|
|
1195
|
+
this.detectArrayMutation(
|
|
1196
|
+
callNode,
|
|
1197
|
+
objectName,
|
|
1198
|
+
methodName as 'push' | 'unshift' | 'splice',
|
|
1199
|
+
module
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// Check for Object.assign() calls
|
|
1204
|
+
if (objectName === 'Object' && methodName === 'assign') {
|
|
1205
|
+
this.detectObjectAssign(callNode, module);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
442
1208
|
// Extract arguments for PASSES_ARGUMENT edges
|
|
443
1209
|
if (callNode.arguments.length > 0) {
|
|
444
1210
|
this.extractArguments(
|
|
@@ -455,8 +1221,8 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
455
1221
|
if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
|
|
456
1222
|
(methodCallbacks as MethodCallbackInfo[]).push({
|
|
457
1223
|
methodCallId,
|
|
458
|
-
callbackLine: arg
|
|
459
|
-
callbackColumn: arg
|
|
1224
|
+
callbackLine: getLine(arg),
|
|
1225
|
+
callbackColumn: getColumn(arg),
|
|
460
1226
|
callbackType: arg.type
|
|
461
1227
|
});
|
|
462
1228
|
}
|
|
@@ -464,14 +1230,52 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
464
1230
|
}
|
|
465
1231
|
}
|
|
466
1232
|
}
|
|
1233
|
+
// REG-117: Nested array mutations like obj.arr.push(item)
|
|
1234
|
+
// object is MemberExpression, property is the method name
|
|
1235
|
+
else if (object.type === 'MemberExpression' && property.type === 'Identifier') {
|
|
1236
|
+
const nestedMember = object as MemberExpression;
|
|
1237
|
+
const methodName = (property as Identifier).name;
|
|
1238
|
+
const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
|
|
1239
|
+
|
|
1240
|
+
if (ARRAY_MUTATION_METHODS.includes(methodName)) {
|
|
1241
|
+
// Extract base object and property from nested MemberExpression
|
|
1242
|
+
const base = nestedMember.object;
|
|
1243
|
+
const prop = nestedMember.property;
|
|
1244
|
+
|
|
1245
|
+
// Only handle single-level nesting: obj.arr.push() or this.items.push()
|
|
1246
|
+
if ((base.type === 'Identifier' || base.type === 'ThisExpression') &&
|
|
1247
|
+
!nestedMember.computed &&
|
|
1248
|
+
prop.type === 'Identifier') {
|
|
1249
|
+
const baseObjectName = base.type === 'Identifier' ? (base as Identifier).name : 'this';
|
|
1250
|
+
const propertyName = (prop as Identifier).name;
|
|
1251
|
+
|
|
1252
|
+
this.detectArrayMutation(
|
|
1253
|
+
callNode,
|
|
1254
|
+
`${baseObjectName}.${propertyName}`, // arrayName for ID purposes
|
|
1255
|
+
methodName as 'push' | 'unshift' | 'splice',
|
|
1256
|
+
module,
|
|
1257
|
+
true, // isNested
|
|
1258
|
+
baseObjectName,
|
|
1259
|
+
propertyName
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
467
1264
|
}
|
|
468
1265
|
},
|
|
469
1266
|
|
|
470
1267
|
// NewExpression: new Foo(), new Function(), new Map(), etc.
|
|
1268
|
+
// Skip if inside function - they will be processed by analyzeFunctionBody with proper scope tracking
|
|
471
1269
|
NewExpression: (path: NodePath) => {
|
|
472
1270
|
const newNode = path.node as NewExpression;
|
|
473
1271
|
const functionParent = path.getFunctionParent();
|
|
474
|
-
|
|
1272
|
+
|
|
1273
|
+
// Skip if inside function - handled by analyzeFunctionBody
|
|
1274
|
+
if (functionParent) {
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
const parentScopeId = module.id;
|
|
475
1279
|
|
|
476
1280
|
// Dedup check
|
|
477
1281
|
const nodeKey = `new:${newNode.start}:${newNode.end}`;
|
|
@@ -484,14 +1288,25 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
484
1288
|
if (newNode.callee.type === 'Identifier') {
|
|
485
1289
|
const callee = newNode.callee as Identifier;
|
|
486
1290
|
const constructorName = callee.name;
|
|
1291
|
+
const newLine = getLine(newNode);
|
|
1292
|
+
const newColumn = getColumn(newNode);
|
|
1293
|
+
|
|
1294
|
+
// Generate ID using centralized IdGenerator
|
|
1295
|
+
const idGenerator = new IdGenerator(scopeTracker);
|
|
1296
|
+
const newCallId = idGenerator.generate(
|
|
1297
|
+
'CALL', `new:${constructorName}`, module.file,
|
|
1298
|
+
newLine, newColumn,
|
|
1299
|
+
callSiteCounterRef,
|
|
1300
|
+
{ useDiscriminator: true, discriminatorKey: `CALL:new:${constructorName}` }
|
|
1301
|
+
);
|
|
487
1302
|
|
|
488
1303
|
(callSites as CallSiteInfo[]).push({
|
|
489
|
-
id:
|
|
1304
|
+
id: newCallId,
|
|
490
1305
|
type: 'CALL',
|
|
491
1306
|
name: constructorName,
|
|
492
1307
|
file: module.file,
|
|
493
|
-
line:
|
|
494
|
-
column:
|
|
1308
|
+
line: newLine,
|
|
1309
|
+
column: newColumn,
|
|
495
1310
|
parentScopeId,
|
|
496
1311
|
targetFunctionName: constructorName,
|
|
497
1312
|
isNew: true // Mark as constructor call
|
|
@@ -507,16 +1322,27 @@ export class CallExpressionVisitor extends ASTVisitor {
|
|
|
507
1322
|
const objectName = (object as Identifier).name;
|
|
508
1323
|
const constructorName = (property as Identifier).name;
|
|
509
1324
|
const fullName = `${objectName}.${constructorName}`;
|
|
1325
|
+
const memberNewLine = getLine(newNode);
|
|
1326
|
+
const memberNewColumn = getColumn(newNode);
|
|
1327
|
+
|
|
1328
|
+
// Generate ID using centralized IdGenerator
|
|
1329
|
+
const idGenerator = new IdGenerator(scopeTracker);
|
|
1330
|
+
const newMethodCallId = idGenerator.generate(
|
|
1331
|
+
'CALL', `new:${fullName}`, module.file,
|
|
1332
|
+
memberNewLine, memberNewColumn,
|
|
1333
|
+
callSiteCounterRef,
|
|
1334
|
+
{ useDiscriminator: true, discriminatorKey: `CALL:new:${fullName}` }
|
|
1335
|
+
);
|
|
510
1336
|
|
|
511
1337
|
(methodCalls as MethodCallInfo[]).push({
|
|
512
|
-
id:
|
|
1338
|
+
id: newMethodCallId,
|
|
513
1339
|
type: 'CALL',
|
|
514
1340
|
name: fullName,
|
|
515
1341
|
object: objectName,
|
|
516
1342
|
method: constructorName,
|
|
517
1343
|
file: module.file,
|
|
518
|
-
line:
|
|
519
|
-
column:
|
|
1344
|
+
line: memberNewLine,
|
|
1345
|
+
column: memberNewColumn,
|
|
520
1346
|
parentScopeId,
|
|
521
1347
|
isNew: true // Mark as constructor call
|
|
522
1348
|
});
|