From b29ff80e3ed8369e4798516163c75d744311102d Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 1 Aug 2024 21:24:55 +0200 Subject: [PATCH] Improved property edges This PR adds major improvements to property edges --- .../cpg/analysis/fsm/DFAOrderEvaluator.kt | 8 +- .../aisec/cpg/passes/UnreachableEOGPass.kt | 22 +- .../cpg/passes/UnreachableEOGPassTest.kt | 57 ++- .../aisec/cpg/frontends/Language.kt | 4 +- .../aisec/cpg/graph/DeclarationHolder.kt | 43 ++- .../aisec/cpg/graph/ExpressionBuilder.kt | 6 +- .../fraunhofer/aisec/cpg/graph/Extensions.kt | 25 +- .../aisec/cpg/graph/MermaidPrinter.kt | 8 +- .../de/fraunhofer/aisec/cpg/graph/Node.kt | 155 +++----- .../aisec/cpg/graph/StatementHolder.kt | 29 +- .../graph/declarations/DeclarationSequence.kt | 16 +- .../cpg/graph/declarations/EnumDeclaration.kt | 4 +- .../graph/declarations/FunctionDeclaration.kt | 27 +- .../FunctionTemplateDeclaration.kt | 9 +- .../graph/declarations/IncludeDeclaration.kt | 21 +- .../declarations/NamespaceDeclaration.kt | 7 +- .../graph/declarations/RecordDeclaration.kt | 20 +- .../declarations/RecordTemplateDeclaration.kt | 14 +- .../graph/declarations/TemplateDeclaration.kt | 14 +- .../TranslationUnitDeclaration.kt | 27 +- .../graph/declarations/ValueDeclaration.kt | 30 +- .../aisec/cpg/graph/edge/Dataflow.kt | 120 ------- .../aisec/cpg/graph/edge/Extensions.kt | 60 ++++ .../aisec/cpg/graph/edge/Properties.kt | 80 ----- .../aisec/cpg/graph/edge/PropertyEdge.kt | 333 ++--------------- .../cpg/graph/edge/PropertyEdgeConverter.kt | 81 ----- .../edge/PropertyEdgeConverterManager.kt | 69 ---- .../aisec/cpg/graph/edge/PropertyEdgeList.kt | 336 ++++++++++++++++++ .../aisec/cpg/graph/edge/ast/AstEdge.kt | 65 ++++ .../cpg/graph/edge/ast/TemplateParameter.kt | 41 +++ .../cpg/graph/edge/flows/ControlDependence.kt | 80 +++++ .../aisec/cpg/graph/edge/flows/Dataflow.kt | 230 ++++++++++++ .../cpg/graph/edge/flows/EvaluationOrder.kt | 84 +++++ .../aisec/cpg/graph/edge/flows/Invoke.kt | 63 ++++ .../cpg/graph/edge/flows/ProgramDependence.kt | 96 +++++ .../aisec/cpg/graph/edge/flows/Usage.kt | 63 ++++ .../graph/statements/DeclarationStatement.kt | 15 +- .../cpg/graph/statements/ForEachStatement.kt | 16 +- .../cpg/graph/statements/LabelStatement.kt | 14 +- .../aisec/cpg/graph/statements/Statement.kt | 4 +- .../cpg/graph/statements/TryStatement.kt | 8 +- .../cpg/graph/statements/expressions/Block.kt | 4 +- .../statements/expressions/CallExpression.kt | 70 ++-- .../statements/expressions/ExpressionList.kt | 11 +- .../expressions/InitializerListExpression.kt | 9 +- .../expressions/NewArrayExpression.kt | 5 +- .../graph/statements/expressions/Reference.kt | 7 +- .../cpg/graph/types/FunctionPointerType.kt | 16 +- .../aisec/cpg/graph/types/ObjectType.kt | 19 +- .../fraunhofer/aisec/cpg/graph/types/Type.kt | 2 + .../aisec/cpg/helpers/EOGWorklist.kt | 5 +- .../aisec/cpg/helpers/IdentitySet.kt | 2 +- .../aisec/cpg/helpers/SubgraphWalker.kt | 13 +- .../de/fraunhofer/aisec/cpg/helpers/Util.kt | 32 +- .../cpg/passes/ControlDependenceGraphPass.kt | 59 ++- .../cpg/passes/ControlFlowSensitiveDFGPass.kt | 10 +- .../de/fraunhofer/aisec/cpg/passes/DFGPass.kt | 30 +- .../aisec/cpg/passes/DynamicInvokeResolver.kt | 5 +- .../cpg/passes/EvaluationOrderGraphPass.kt | 86 ++--- .../cpg/passes/ProgramDependenceGraphPass.kt | 21 +- .../aisec/cpg/passes/SymbolResolver.kt | 2 +- .../passes/inference/DFGFunctionSummaries.kt | 2 +- .../aisec/cpg/passes/inference/Inference.kt | 2 +- .../enhancements/DFGFunctionSummariesTest.kt | 6 +- .../aisec/cpg/graph/ExpressionBuilderTest.kt | 8 +- .../aisec/cpg/graph/edge/ExtensionsTest.kt | 60 ++++ .../cpg/graph/edge/PropertyEdgesTest.kt} | 26 +- .../graph/edge/flows/ControlDependenceTest.kt | 51 +++ .../cpg/graph/edge/flows/DataflowTest.kt | 51 +++ .../graph/edge/flows/ProgramDependenceTest.kt | 104 ++++++ .../passes/ControlFlowSensitiveDFGPassTest.kt | 6 +- .../passes/ProgramDependenceGraphPassTest.kt | 83 ++--- .../aisec/cpg/frontends/TestLanguage.kt | 15 +- .../aisec/cpg/frontends/cxx/CPPLanguage.kt | 8 +- .../cpg/frontends/cxx/DeclarationHandler.kt | 2 +- .../cpg/frontends/cxx/InitializerHandler.kt | 7 +- .../aisec/cpg/PerformanceRegressionTest.kt | 18 +- .../aisec/cpg/enhancements/EOGTest.kt | 27 +- .../enhancements/calls/FunctionPointerTest.kt | 3 +- .../templates/ClassTemplateTest.kt | 55 +-- .../templates/FunctionTemplateTest.kt | 17 +- .../aisec/cpg/frontends/cxx/CDataflowTest.kt | 4 +- .../golang/GoLanguageFrontendTest.kt | 3 +- .../cpg/frontends/java/DeclarationHandler.kt | 4 +- .../cpg/frontends/java/ExpressionHandler.kt | 5 +- .../aisec/cpg/enhancements/EOGTest.kt | 46 ++- .../frontends/python/PythonFrontendTest.kt | 3 +- .../aisec/cpg_vis_neo4j/Application.kt | 2 + .../aisec/cpg_vis_neo4j/ApplicationTest.kt | 3 +- .../aisec/cpg_vis_neo4j/Neo4JTest.kt | 18 - 90 files changed, 1978 insertions(+), 1473 deletions(-) delete mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Dataflow.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Extensions.kt delete mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Properties.kt delete mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverter.kt delete mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverterManager.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeList.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/AstEdge.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/TemplateParameter.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependence.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Dataflow.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/EvaluationOrder.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Invoke.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependence.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Usage.kt create mode 100644 cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ExtensionsTest.kt rename cpg-core/src/{main/kotlin/de/fraunhofer/aisec/cpg/graph/EdgeProperty.kt => test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgesTest.kt} (57%) create mode 100644 cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependenceTest.kt create mode 100644 cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/DataflowTest.kt create mode 100644 cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependenceTest.kt diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt index a5467d553c..27476e882c 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt @@ -28,8 +28,6 @@ package de.fraunhofer.aisec.cpg.analysis.fsm import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression @@ -435,14 +433,12 @@ open class DFAOrderEvaluator( val outNodes = mutableListOf() outNodes += if (eliminateUnreachableCode) { - PropertyEdge.unwrap( - node.nextEOGEdges.filter { e -> e.getProperty(Properties.UNREACHABLE) != true } - ) + node.nextEOGEdges.filter { e -> e.unreachable != true }.map { it.end } } else { node.nextEOG } - if (outNodes.size == 1 && node.nextEOG.size == 1) { + if (outNodes.size == 1 && node.nextEOGEdges.size == 1) { // We only have one node following this node, so we // simply propagate the current eogPath to the next node. outNodes[0].addEogPath(eogPath) diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt index dfc0c45084..9a34281d1a 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt @@ -30,8 +30,8 @@ import de.fraunhofer.aisec.cpg.analysis.ValueEvaluator import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.flows.EvaluationOrder import de.fraunhofer.aisec.cpg.graph.statements.IfStatement import de.fraunhofer.aisec.cpg.graph.statements.WhileStatement import de.fraunhofer.aisec.cpg.helpers.* @@ -39,7 +39,7 @@ import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn /** * A [Pass] which uses a simple logic to determine constant values and mark unreachable code regions - * by setting the [Properties.UNREACHABLE] property of an eog-edge to true. + * by setting the [EvaluationOrder.unreachable] property to true. */ @DependsOn(ControlFlowSensitiveDFGPass::class) class UnreachableEOGPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { @@ -68,7 +68,7 @@ class UnreachableEOGPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { for ((key, value) in finalState) { if (value.elements == Reachability.UNREACHABLE) { - key.addProperty(Properties.UNREACHABLE, true) + (key as? EvaluationOrder)?.unreachable = true } } } @@ -126,13 +126,13 @@ private fun handleIfStatement( val (unreachableEdge, remainingEdges) = if (evalResult is Boolean && evalResult == true) { Pair( - n.nextEOGEdges.firstOrNull { e -> e.getProperty(Properties.INDEX) == 1 }, - n.nextEOGEdges.filter { e -> e.getProperty(Properties.INDEX) != 1 } + n.nextEOGEdges.firstOrNull { e -> e.index == 1 }, + n.nextEOGEdges.filter { e -> e.index != 1 } ) } else if (evalResult is Boolean && evalResult == false) { Pair( - n.nextEOGEdges.firstOrNull { e -> e.getProperty(Properties.INDEX) == 0 }, - n.nextEOGEdges.filter { e -> e.getProperty(Properties.INDEX) != 0 } + n.nextEOGEdges.firstOrNull { e -> e.index == 0 }, + n.nextEOGEdges.filter { e -> e.index != 0 } ) } else { Pair(null, n.nextEOGEdges) @@ -173,13 +173,13 @@ private fun handleWhileStatement( val (unreachableEdge, remainingEdges) = if (evalResult is Boolean && evalResult == true) { Pair( - n.nextEOGEdges.firstOrNull { e -> e.getProperty(Properties.INDEX) == 1 }, - n.nextEOGEdges.filter { e -> e.getProperty(Properties.INDEX) != 1 } + n.nextEOGEdges.firstOrNull { e -> e.index == 1 }, + n.nextEOGEdges.filter { e -> e.index != 1 } ) } else if (evalResult is Boolean && evalResult == false) { Pair( - n.nextEOGEdges.firstOrNull { e -> e.getProperty(Properties.INDEX) == 0 }, - n.nextEOGEdges.filter { e -> e.getProperty(Properties.INDEX) != 0 } + n.nextEOGEdges.firstOrNull { e -> e.index == 0 }, + n.nextEOGEdges.filter { e -> e.index != 0 } ) } else { Pair(null, n.nextEOGEdges) diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPassTest.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPassTest.kt index 2b0f0128fa..3519e55a39 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPassTest.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPassTest.kt @@ -27,7 +27,6 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.testcases.Passes import kotlin.test.* import org.junit.jupiter.api.BeforeAll @@ -51,7 +50,7 @@ class UnreachableEOGPassTest { assertNotNull(ifStatement) for (edge in ifStatement.nextEOGEdges) { - assertFalse(edge.getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(edge.unreachable) } } @@ -66,43 +65,43 @@ class UnreachableEOGPassTest { // Check if the then-branch is set as reachable including all the edges until reaching the // print val thenDecl = ifStatement.nextEOGEdges[0] - assertFalse(thenDecl.getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(thenDecl.unreachable) assertEquals(1, thenDecl.end.nextEOGEdges.size) // The "++" val incOp = thenDecl.end.nextEOGEdges[0] - assertFalse(incOp.getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(incOp.unreachable) assertEquals(1, incOp.end.nextEOGEdges.size) // The block val thenCompound = incOp.end.nextEOGEdges[0] - assertFalse(thenCompound.getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(thenCompound.unreachable) assertEquals(1, thenCompound.end.nextEOGEdges.size) // There's the outgoing EOG edge to the statement after the branching val thenExit = thenCompound.end.nextEOGEdges[0] - assertFalse(thenExit.getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(thenExit.unreachable) // Check if the else-branch is set as unreachable including all the edges until reaching the // print val elseDecl = ifStatement.nextEOGEdges[1] - assertTrue(elseDecl.getProperty(Properties.UNREACHABLE) as Boolean) + assertTrue(elseDecl.unreachable) assertEquals(1, elseDecl.end.nextEOGEdges.size) // The "--" val decOp = elseDecl.end.nextEOGEdges[0] - assertTrue(decOp.getProperty(Properties.UNREACHABLE) as Boolean) + assertTrue(decOp.unreachable) assertEquals(1, decOp.end.nextEOGEdges.size) // The block val elseCompound = decOp.end.nextEOGEdges[0] - assertTrue(elseCompound.getProperty(Properties.UNREACHABLE) as Boolean) + assertTrue(elseCompound.unreachable) assertEquals(1, elseCompound.end.nextEOGEdges.size) // There's the outgoing EOG edge to the statement after the branching val elseExit = elseCompound.end.nextEOGEdges[0] - assertTrue(elseExit.getProperty(Properties.UNREACHABLE) as Boolean) + assertTrue(elseExit.unreachable) // After the branching, it's reachable again. Check that we found the merge node and that we // continue with reachable edges. assertEquals(thenExit.end, elseExit.end) val mergeNode = thenExit.end assertEquals(1, mergeNode.nextEOGEdges.size) - assertFalse(mergeNode.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(mergeNode.nextEOGEdges[0].unreachable) } @Test @@ -113,8 +112,8 @@ class UnreachableEOGPassTest { val ifStatement = method.ifs.firstOrNull() assertNotNull(ifStatement) - assertFalse(ifStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) - assertTrue(ifStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(ifStatement.nextEOGEdges[1].unreachable) + assertTrue(ifStatement.nextEOGEdges[0].unreachable) } @Test @@ -125,8 +124,8 @@ class UnreachableEOGPassTest { val ifStatement = method.ifs.firstOrNull() assertNotNull(ifStatement) - assertFalse(ifStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) - assertTrue(ifStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(ifStatement.nextEOGEdges[0].unreachable) + assertTrue(ifStatement.nextEOGEdges[1].unreachable) } @Test @@ -137,8 +136,8 @@ class UnreachableEOGPassTest { val ifStatement = method.ifs.firstOrNull() assertNotNull(ifStatement) - assertFalse(ifStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) - assertTrue(ifStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(ifStatement.nextEOGEdges[1].unreachable) + assertTrue(ifStatement.nextEOGEdges[0].unreachable) } @Test @@ -149,8 +148,8 @@ class UnreachableEOGPassTest { val whileStatement = method.whileLoops.firstOrNull() assertNotNull(whileStatement) - assertFalse(whileStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) - assertTrue(whileStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(whileStatement.nextEOGEdges[0].unreachable) + assertTrue(whileStatement.nextEOGEdges[1].unreachable) } @Test @@ -161,8 +160,8 @@ class UnreachableEOGPassTest { val whileStatement = method.whileLoops.firstOrNull() assertNotNull(whileStatement) - assertFalse(whileStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) - assertFalse(whileStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(whileStatement.nextEOGEdges[0].unreachable) + assertFalse(whileStatement.nextEOGEdges[1].unreachable) } @Test @@ -173,8 +172,8 @@ class UnreachableEOGPassTest { val whileStatement = method.whileLoops.firstOrNull() assertNotNull(whileStatement) - assertFalse(whileStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) - assertTrue(whileStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(whileStatement.nextEOGEdges[0].unreachable) + assertTrue(whileStatement.nextEOGEdges[1].unreachable) } @Test @@ -185,8 +184,8 @@ class UnreachableEOGPassTest { val whileStatement = method.whileLoops.firstOrNull() assertNotNull(whileStatement) - assertFalse(whileStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) - assertTrue(whileStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(whileStatement.nextEOGEdges[1].unreachable) + assertTrue(whileStatement.nextEOGEdges[0].unreachable) } @Test @@ -197,8 +196,8 @@ class UnreachableEOGPassTest { val whileStatement = method.whileLoops.firstOrNull() assertNotNull(whileStatement) - assertFalse(whileStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) - assertTrue(whileStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(whileStatement.nextEOGEdges[1].unreachable) + assertTrue(whileStatement.nextEOGEdges[0].unreachable) } @Test @@ -209,7 +208,7 @@ class UnreachableEOGPassTest { val whileStatement = method.whileLoops.firstOrNull() assertNotNull(whileStatement) - assertFalse(whileStatement.nextEOGEdges[1].getProperty(Properties.UNREACHABLE) as Boolean) - assertFalse(whileStatement.nextEOGEdges[0].getProperty(Properties.UNREACHABLE) as Boolean) + assertFalse(whileStatement.nextEOGEdges[1].unreachable) + assertFalse(whileStatement.nextEOGEdges[0].unreachable) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index 5a36b6a880..ba4576b104 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -34,11 +34,11 @@ import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.edge.ast.TemplateParameters import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.graph.unknownType -import de.fraunhofer.aisec.cpg.passes.SymbolResolver import java.io.File import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor @@ -322,7 +322,7 @@ abstract class Language> : Node() { // matches val source = result.source if (this is HasTemplates && source is CallExpression) { - source.templateParameterEdges = mutableListOf() + source.templateParameterEdges = TemplateParameters(source) val (ok, candidates) = this.handleTemplateFunctionCalls( null, diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationHolder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationHolder.kt index 129111ef4a..47c70b643f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationHolder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationHolder.kt @@ -26,8 +26,10 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.graph.declarations.Declaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeList +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdges interface DeclarationHolder { /** @@ -44,8 +46,12 @@ interface DeclarationHolder { } } - fun addIfNotContains( - collection: MutableCollection>, + fun > addIfNotContains(collection: AstEdges, declaration: T) { + addIfNotContains(collection, declaration, true) + } + + fun > addIfNotContains( + collection: PropertyEdgeList, declaration: T ) { addIfNotContains(collection, declaration, true) @@ -59,28 +65,31 @@ interface DeclarationHolder { * @param the type of the declaration * @param outgoing whether the property is outgoing */ - fun addIfNotContains( - collection: MutableCollection>, + fun > addIfNotContains( + collection: PropertyEdgeList, declaration: T, outgoing: Boolean ) { - // create a new property edge - val propertyEdge = - if (outgoing) PropertyEdge(this as Node, declaration) - else PropertyEdge(declaration, this as T) - - // set the index property - propertyEdge.addProperty(Properties.INDEX, collection.size) var contains = false for (element in collection) { - if (element.end == propertyEdge.end) { - contains = true - break + if (outgoing) { + if (element.end == declaration) { + contains = true + break + } + } else { + if (element.start == declaration) { + contains = true + break + } } } - if (!contains) { - collection.add(propertyEdge) + + if (contains) { + return } + + collection.add(declaration) } val declarations: List diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt index c5f956a757..831d41146f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt @@ -25,12 +25,10 @@ */ package de.fraunhofer.aisec.cpg.graph -import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators -import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.graph.Node.Companion.EMPTY_NAME import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log -import de.fraunhofer.aisec.cpg.graph.edge.ContextSensitiveDataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.ContextSensitiveDataflow import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.AssignExpression import de.fraunhofer.aisec.cpg.graph.types.ProblemType @@ -567,7 +565,7 @@ fun Literal.duplicate(implicit: Boolean): Literal { duplicate.file = this.file duplicate.name = this.name.clone() for (next in this.nextDFGEdges) { - duplicate.addNextDFG( + duplicate.nextDFGEdges.add( next.end, next.granularity, (next as? ContextSensitiveDataflow)?.callingContext diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt index 4200759a06..776977cc7f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt @@ -27,7 +27,6 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.statements.ForEachStatement import de.fraunhofer.aisec.cpg.graph.statements.ForStatement @@ -330,20 +329,14 @@ fun Node.followNextEOGEdgesUntilHit(predicate: (Node) -> Boolean): FulfilledAndF val currentPath = worklist.removeFirst() // The last node of the path is where we continue. We get all of its outgoing DFG edges and // follow them - if ( - currentPath.last().nextEOGEdges.none { it.getProperty(Properties.UNREACHABLE) != true } - ) { + if (currentPath.last().nextEOGEdges.none { it.unreachable != true }) { // No further nodes in the path and the path criteria are not satisfied. failedPaths.add(currentPath) continue // Don't add this path anymore. The requirement is satisfied. } for (next in - currentPath - .last() - .nextEOGEdges - .filter { it.getProperty(Properties.UNREACHABLE) != true } - .map { it.end }) { + currentPath.last().nextEOGEdges.filter { it.unreachable != true }.map { it.end }) { // Copy the path for each outgoing DFG edge and add the next node val nextPath = mutableListOf() nextPath.addAll(currentPath) @@ -388,20 +381,14 @@ fun Node.followPrevEOGEdgesUntilHit(predicate: (Node) -> Boolean): FulfilledAndF val currentPath = worklist.removeFirst() // The last node of the path is where we continue. We get all of its outgoing DFG edges and // follow them - if ( - currentPath.last().prevEOGEdges.none { it.getProperty(Properties.UNREACHABLE) != true } - ) { + if (currentPath.last().prevEOGEdges.none { it.unreachable != true }) { // No further nodes in the path and the path criteria are not satisfied. failedPaths.add(currentPath) continue // Don't add this path anymore. The requirement is satisfied. } for (next in - currentPath - .last() - .prevEOGEdges - .filter { it.getProperty(Properties.UNREACHABLE) != true } - .map { it.start }) { + currentPath.last().prevEOGEdges.filter { it.unreachable != true }.map { it.start }) { // Copy the path for each outgoing DFG edge and add the next node val nextPath = mutableListOf() nextPath.addAll(currentPath) @@ -432,7 +419,7 @@ fun Node.followPrevEOGEdgesUntilHit(predicate: (Node) -> Boolean): FulfilledAndF fun Node.followNextEOG(predicate: (PropertyEdge<*>) -> Boolean): List>? { val path = mutableListOf>() - for (edge in this.nextEOGEdges.filter { it.getProperty(Properties.UNREACHABLE) != true }) { + for (edge in this.nextEOGEdges.filter { it.unreachable != true }) { val target = edge.end path.add(edge) @@ -462,7 +449,7 @@ fun Node.followNextEOG(predicate: (PropertyEdge<*>) -> Boolean): List) -> Boolean): List>? { val path = mutableListOf>() - for (edge in this.prevEOGEdges.filter { it.getProperty(Properties.UNREACHABLE) != true }) { + for (edge in this.prevEOGEdges.filter { it.unreachable != true }) { val source = edge.start path.add(edge) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/MermaidPrinter.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/MermaidPrinter.kt index 58bbd68617..0b7419be0e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/MermaidPrinter.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/MermaidPrinter.kt @@ -25,9 +25,9 @@ */ package de.fraunhofer.aisec.cpg.graph -import de.fraunhofer.aisec.cpg.graph.edge.Dataflow -import de.fraunhofer.aisec.cpg.graph.edge.PartialDataflowGranularity import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.PartialDataflowGranularity import de.fraunhofer.aisec.cpg.helpers.identitySetOf import kotlin.reflect.KProperty1 @@ -54,8 +54,8 @@ fun Node.printEOG(maxConnections: Int = 25): String { * need to return a list of edges (as a [PropertyEdge]) beginning from this node. */ fun > Node.printGraph( - nextEdgeGetter: KProperty1>, - prevEdgeGetter: KProperty1>, + nextEdgeGetter: KProperty1>, + prevEdgeGetter: KProperty1>, maxConnections: Int = 25 ): String { val builder = StringBuilder() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt index d9eca8716d..69eee193fb 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt @@ -27,16 +27,22 @@ package de.fraunhofer.aisec.cpg.graph import com.fasterxml.jackson.annotation.JsonBackReference import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonManagedReference import de.fraunhofer.aisec.cpg.PopulatedByPass import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager -import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.frontends.Language -import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edge.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope -import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContext +import de.fraunhofer.aisec.cpg.graph.edge.flows.ContextSensitiveDataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.ControlDependences +import de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflows +import de.fraunhofer.aisec.cpg.graph.edge.flows.EvaluationOrders +import de.fraunhofer.aisec.cpg.graph.edge.flows.FullDataflowGranularity +import de.fraunhofer.aisec.cpg.graph.edge.flows.Granularity +import de.fraunhofer.aisec.cpg.graph.edge.flows.ProgramDependences +import de.fraunhofer.aisec.cpg.graph.edge.flows.default +import de.fraunhofer.aisec.cpg.graph.edge.flows.full import de.fraunhofer.aisec.cpg.graph.scopes.Scope import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.helpers.neo4j.LocationConverter @@ -53,7 +59,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory /** The base class for all graph objects that are going to be persisted in the database. */ -open class Node : +abstract class Node : IVisitable, Persistable, LanguageProvider, @@ -113,13 +119,15 @@ open class Node : /** Incoming control flow edges. */ @Relationship(value = "EOG", direction = Relationship.Direction.INCOMING) @PopulatedByPass(EvaluationOrderGraphPass::class) - var prevEOGEdges: MutableList> = ArrayList() + @JsonIgnore + var prevEOGEdges = EvaluationOrders(this, outgoing = false) protected set /** Outgoing control flow edges. */ @Relationship(value = "EOG", direction = Relationship.Direction.OUTGOING) @PopulatedByPass(EvaluationOrderGraphPass::class) - var nextEOGEdges: MutableList> = ArrayList() + @JsonManagedReference + var nextEOGEdges = EvaluationOrders(this) protected set /** @@ -128,7 +136,8 @@ open class Node : */ @PopulatedByPass(ControlDependenceGraphPass::class) @Relationship(value = "CDG", direction = Relationship.Direction.OUTGOING) - var nextCDGEdges: MutableList> = ArrayList() + var nextCDGEdges: ControlDependences = + ControlDependences(this, mirrorProperty = Node::prevCDGEdges, outgoing = true) protected set var nextCDG by PropertyEdgeDelegate(Node::nextCDGEdges, true) @@ -139,7 +148,8 @@ open class Node : */ @PopulatedByPass(ControlDependenceGraphPass::class) @Relationship(value = "CDG", direction = Relationship.Direction.INCOMING) - var prevCDGEdges: MutableList> = ArrayList() + var prevCDGEdges: ControlDependences = + ControlDependences(this, Node::nextCDGEdges, outgoing = false) protected set var prevCDG by PropertyEdgeDelegate(Node::prevCDGEdges, false) @@ -155,28 +165,33 @@ open class Node : * persistence. Therefore, this property is a `var` and not a `val`. */ @Relationship("AST") + @JsonIgnore var astChildren: List = listOf() get() = SubgraphWalker.getAstChildren(this) /** Virtual property for accessing [prevEOGEdges] without property edges. */ @PopulatedByPass(EvaluationOrderGraphPass::class) - var prevEOG: List by PropertyEdgeDelegate(Node::prevEOGEdges, false) + var prevEOG by PropertyEdgeDelegate(Node::prevEOGEdges, false) /** Virtual property for accessing [nextEOGEdges] without property edges. */ @PopulatedByPass(EvaluationOrderGraphPass::class) - var nextEOG: List by PropertyEdgeDelegate(Node::nextEOGEdges) + var nextEOG by PropertyEdgeDelegate(Node::nextEOGEdges) /** Incoming data flow edges */ @Relationship(value = "DFG", direction = Relationship.Direction.INCOMING) @PopulatedByPass(DFGPass::class, ControlFlowSensitiveDFGPass::class) - var prevDFGEdges: MutableList = mutableListOf() + @JsonIgnore + var prevDFGEdges: Dataflows = Dataflows(this, Node::nextDFGEdges, outgoing = false) protected set /** Virtual property for accessing [prevDFGEdges] without property edges. */ @PopulatedByPass(DFGPass::class, ControlFlowSensitiveDFGPass::class) - var prevDFG: MutableSet by PropertyEdgeSetDelegate(Node::prevDFGEdges, false) + var prevDFG by PropertyEdgeSetDelegate(Node::prevDFGEdges, false) - /** Virtual property for accessing [nextDFGEdges] that have a [FullDataflowGranularity]. */ + /** + * Virtual property for accessing [nextDFGEdges] that have a + * [de.fraunhofer.aisec.cpg.graph.edge.flows.FullDataflowGranularity]. + */ @PopulatedByPass(DFGPass::class, ControlFlowSensitiveDFGPass::class) val prevFullDFG: List get() { @@ -188,14 +203,17 @@ open class Node : /** Outgoing data flow edges */ @PopulatedByPass(DFGPass::class, ControlFlowSensitiveDFGPass::class) @Relationship(value = "DFG", direction = Relationship.Direction.OUTGOING) - var nextDFGEdges: MutableList = mutableListOf() + var nextDFGEdges: Dataflows = Dataflows(this, Node::prevDFGEdges, outgoing = true) protected set /** Virtual property for accessing [nextDFGEdges] without property edges. */ @PopulatedByPass(DFGPass::class, ControlFlowSensitiveDFGPass::class) - var nextDFG: MutableSet by PropertyEdgeSetDelegate(Node::nextDFGEdges, true) + var nextDFG by PropertyEdgeSetDelegate(Node::nextDFGEdges, true) - /** Virtual property for accessing [nextDFGEdges] that have a [FullDataflowGranularity]. */ + /** + * Virtual property for accessing [nextDFGEdges] that have a + * [de.fraunhofer.aisec.cpg.graph.edge.flows.FullDataflowGranularity]. + */ @PopulatedByPass(DFGPass::class, ControlFlowSensitiveDFGPass::class) val nextFullDFG: List get() { @@ -205,21 +223,17 @@ open class Node : /** Outgoing Program Dependence Edges. */ @PopulatedByPass(ProgramDependenceGraphPass::class) @Relationship(value = "PDG", direction = Relationship.Direction.OUTGOING) - var nextPDGEdges: MutableSet> = mutableSetOf() + var nextPDGEdges: ProgramDependences = + ProgramDependences(this, Node::prevPDGEdges, outgoing = false) protected set - /** Virtual property for accessing the children of the Program Dependence Graph (PDG). */ - var nextPDG: MutableSet by PropertyEdgeSetDelegate(Node::nextPDGEdges, true) - /** Incoming Program Dependence Edges. */ @PopulatedByPass(ProgramDependenceGraphPass::class) @Relationship(value = "PDG", direction = Relationship.Direction.INCOMING) - var prevPDGEdges: MutableSet> = mutableSetOf() + var prevPDGEdges: ProgramDependences = + ProgramDependences(this, Node::nextPDGEdges, outgoing = false) protected set - /** Virtual property for accessing the parents of the Program Dependence Graph (PDG). */ - var prevPDG: MutableSet by PropertyEdgeSetDelegate(Node::prevPDGEdges, false) - /** * If a node is marked as being inferred, it means that it was created artificially and does not * necessarily have a real counterpart in the scanned source code. However, the nodes @@ -250,54 +264,25 @@ open class Node : private fun removePrevEOGEntries(prevEOGs: List) { for (n in prevEOGs) { - val remove = PropertyEdge.findPropertyEdgesByPredicate(prevEOGEdges) { it.start === n } + val remove = prevEOGEdges.filter { it.start === n } prevEOGEdges.removeAll(remove) } } - fun addPrevEOG(propertyEdge: PropertyEdge) { - prevEOGEdges.add(propertyEdge) - } - - fun addNextEOG(propertyEdge: PropertyEdge) { - nextEOGEdges.add(propertyEdge) - } - - fun clearNextEOG() { - nextEOGEdges.clear() - } - - /** Adds a [Dataflow] edge from this node to [next], with the given [Granularity]. */ - fun addNextDFG( - next: Node, - granularity: Granularity = default(), - callingContext: CallingContext? = null, - ) { - val edge = - if (callingContext != null) { - ContextSensitiveDataflow(this, next, callingContext, granularity) - } else { - Dataflow(this, next, granularity) - } - nextDFGEdges.add(edge) - next.prevDFGEdges.add(edge) - } - fun removeNextDFG(next: Node?) { if (next != null) { - val thisRemove = - PropertyEdge.findPropertyEdgesByPredicate(nextDFGEdges) { it.end === next } + val thisRemove = nextDFGEdges.filter { it.end === next } nextDFGEdges.removeAll(thisRemove) - val nextRemove = - PropertyEdge.findPropertyEdgesByPredicate(next.prevDFGEdges) { it.start == this } + val nextRemove = next.prevDFGEdges.filter { it.start == this } next.prevDFGEdges.removeAll(nextRemove) } } /** - * Adds a [Dataflow] edge from [prev] node to this node, with the given [Granularity] and - * [CallingContext]. + * Adds a [de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflow] edge from [prev] node to this + * node, with the given [de.fraunhofer.aisec.cpg.graph.edge.flows.Granularity] and + * [de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContext]. */ open fun addPrevDFG( prev: Node, @@ -311,21 +296,12 @@ open class Node : Dataflow(prev, this, granularity) } prevDFGEdges.add(edge) - prev.nextDFGEdges.add(edge) - } - - fun addPrevCDG( - prev: Node, - properties: MutableMap = EnumMap(Properties::class.java) - ) { - val edge = PropertyEdge(prev, this, properties) - prevCDGEdges.add(edge) - prev.nextCDGEdges.add(edge) } /** - * Adds a [Dataflow] edge from all [prev] nodes to this node, with the given [Granularity] and - * [CallingContext] if applicable. + * Adds a [de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflow] edge from all [prev] nodes to this + * node, with the given [de.fraunhofer.aisec.cpg.graph.edge.flows.Granularity] and + * [de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContext] if applicable. */ fun addAllPrevDFG( prev: Collection, @@ -335,27 +311,12 @@ open class Node : prev.forEach { addPrevDFG(it, granularity, callingContext) } } - fun addAllPrevPDG(prev: Collection, dependenceType: DependenceType) { - addAllPrevPDGEdges(prev.map { PropertyEdge(it, this) }, dependenceType) - } - - fun addAllPrevPDGEdges(prev: Collection>, dependenceType: DependenceType) { - prev.forEach { - val edge = PropertyEdge(it).apply { addProperty(Properties.DEPENDENCE, dependenceType) } - this.prevPDGEdges.add(edge) - val other = if (it.start != this) it.start else it.end - other.nextPDGEdges.add(edge) - } - } - fun removePrevDFG(prev: Node?) { if (prev != null) { - val thisRemove = - PropertyEdge.findPropertyEdgesByPredicate(prevDFGEdges) { it.start === prev } + val thisRemove = prevDFGEdges.filter { it.start === prev } prevDFGEdges.removeAll(thisRemove) - val prevRemove = - PropertyEdge.findPropertyEdgesByPredicate(prev.nextDFGEdges) { it.end === this } + val prevRemove = prev.nextDFGEdges.filter { it.end === this } prev.nextDFGEdges.removeAll(prevRemove) } } @@ -387,29 +348,25 @@ open class Node : */ fun disconnectFromGraph() { for (n in nextDFGEdges) { - val remove = - PropertyEdge.findPropertyEdgesByPredicate(n.end.prevDFGEdges) { it.start == this } + val remove = n.end.prevDFGEdges.filter { it.start == this } n.end.prevDFGEdges.removeAll(remove) } nextDFGEdges.clear() for (n in prevDFGEdges) { - val remove = - PropertyEdge.findPropertyEdgesByPredicate(n.start.nextDFGEdges) { it.end == this } + val remove = n.start.nextDFGEdges.filter { it.end == this } n.start.nextDFGEdges.removeAll(remove) } prevDFGEdges.clear() for (n in nextEOGEdges) { - val remove = - PropertyEdge.findPropertyEdgesByPredicate(n.end.prevEOGEdges) { it.start == this } + val remove = n.end.prevEOGEdges.filter { it.start == this } n.end.prevEOGEdges.removeAll(remove) } nextEOGEdges.clear() for (n in prevEOGEdges) { - val remove = - PropertyEdge.findPropertyEdgesByPredicate(n.start.nextEOGEdges) { it.end == this } + val remove = n.start.nextEOGEdges.filter { it.end == this } n.start.nextEOGEdges.removeAll(remove) } prevEOGEdges.clear() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt index 39d9492be5..34fc694561 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt @@ -25,11 +25,8 @@ */ package de.fraunhofer.aisec.cpg.graph -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.wrap -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdges import de.fraunhofer.aisec.cpg.graph.statements.Statement /** @@ -43,7 +40,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.Statement */ interface StatementHolder : Holder { /** List of statements as property edges. */ - var statementEdges: MutableList> + var statementEdges: AstEdges> /** * Virtual property to access [statementEdges] without property edges. @@ -52,10 +49,10 @@ interface StatementHolder : Holder { */ var statements: List get() { - return unwrap(statementEdges) + return statementEdges.unwrap() } set(value) { - statementEdges = wrap(value, this as Node) + statementEdges.resetTo(value) } /** @@ -66,21 +63,7 @@ interface StatementHolder : Holder { * @param s the statement */ fun addStatement(s: Statement) { - val propertyEdge = PropertyEdge((this as Node), s) - propertyEdge.addProperty(Properties.INDEX, statementEdges.size) - statementEdges.add(propertyEdge) - } - - /** Inserts the statement [s] before the statement specified in [before]. */ - fun insertStatementBefore(s: Statement, beforeStmt: Statement) { - val statements = this.statements - val idx = statements.indexOf(beforeStmt) - if (idx != -1) { - val before = statements.subList(0, idx) - val after = statements.subList(idx, statements.size) - - this.statements = listOf(*before.toTypedArray(), s, *after.toTypedArray()) - } + statementEdges.add(s) } override operator fun plusAssign(node: Statement) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.kt index 60261034dc..59bed72829 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.kt @@ -26,9 +26,6 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.DeclarationHolder -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate -import org.neo4j.ogm.annotation.Relationship /** * This represents a sequence of one or more declaration(s). The purpose of this node is primarily @@ -37,18 +34,15 @@ import org.neo4j.ogm.annotation.Relationship * the translation unit. It should NOT end up in the final graph. */ class DeclarationSequence : Declaration(), DeclarationHolder { - @Relationship(value = "CHILDREN", direction = Relationship.Direction.OUTGOING) - val childEdges: MutableList> = mutableListOf() - - val children: List by PropertyEdgeDelegate(DeclarationSequence::childEdges) + val children = mutableListOf() override fun addDeclaration(declaration: Declaration) { if (declaration is DeclarationSequence) { for (declarationChild in declaration.children) { - addIfNotContains(childEdges, declarationChild) + addIfNotContains(children, declarationChild) } } else { - addIfNotContains(childEdges, declaration) + addIfNotContains(children, declaration) } } @@ -57,10 +51,10 @@ class DeclarationSequence : Declaration(), DeclarationHolder { } val isSingle: Boolean - get() = childEdges.size == 1 + get() = children.size == 1 fun first(): Declaration { - return childEdges[0].end + return children.first() } operator fun plusAssign(declaration: Declaration) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt index de9e48eff1..01bfbffcbd 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt @@ -26,15 +26,15 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship class EnumDeclaration : RecordDeclaration() { @Relationship(value = "ENTRIES", direction = Relationship.Direction.OUTGOING) @AST - var entryEdges: MutableList> = ArrayList() + var entryEdges = astEdgesOf() var entries by PropertyEdgeDelegate(EnumDeclaration::entryEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt index a1bb9f7fa3..527ac78a50 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt @@ -26,12 +26,10 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.* -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* @@ -46,20 +44,19 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, EOGStart /** * Classes and Structs can be declared inside a function and are only valid within the function. */ - @Relationship(value = "RECORDS", direction = Relationship.Direction.OUTGOING) - var recordEdges = mutableListOf>() + // TODO: this should actually not be here, since in the AST the record is probably part of a + // declarationstatement + // and the symbol is part of the scope + var records = mutableListOf() /** The list of function parameters. */ @Relationship(value = "PARAMETERS", direction = Relationship.Direction.OUTGOING) @AST - var parameterEdges = mutableListOf>() + var parameterEdges = astEdgesOf() /** Virtual property for accessing [parameterEdges] without property edges. */ var parameters by PropertyEdgeDelegate(FunctionDeclaration::parameterEdges) - /** Virtual property for accessing [parameterEdges] without property edges. */ - var records by PropertyEdgeDelegate(FunctionDeclaration::recordEdges) - @Relationship(value = "THROWS_TYPES", direction = Relationship.Direction.OUTGOING) var throwsTypes = mutableListOf() @@ -157,16 +154,6 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, EOGStart val signatureTypes: List get() = parameters.map { it.type } - fun addParameter(parameterDeclaration: ParameterDeclaration) { - val propertyEdge = PropertyEdge(this, parameterDeclaration) - propertyEdge.addProperty(Properties.INDEX, parameters.size) - parameterEdges.add(propertyEdge) - } - - fun removeParameter(parameterDeclaration: ParameterDeclaration) { - parameterEdges.removeIf { it.end == parameterDeclaration } - } - override fun toString(): String { return ToStringBuilder(this, TO_STRING_STYLE) .appendSuper(super.toString()) @@ -199,7 +186,7 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, EOGStart } if (declaration is RecordDeclaration) { - addIfNotContains(recordEdges, declaration) + addIfNotContains(records, declaration) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt index 3ebaa16e33..56a9ed8247 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt @@ -26,10 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import java.util.* import org.neo4j.ogm.annotation.Relationship @@ -42,7 +41,7 @@ class FunctionTemplateDeclaration : TemplateDeclaration() { */ @Relationship(value = "REALIZATION", direction = Relationship.Direction.OUTGOING) @AST - private val realizationEdges: MutableList> = ArrayList() + private val realizationEdges = astEdgesOf() val realization: List by PropertyEdgeDelegate(FunctionTemplateDeclaration::realizationEdges) @@ -51,9 +50,7 @@ class FunctionTemplateDeclaration : TemplateDeclaration() { get() = ArrayList(realization) fun addRealization(realizedFunction: FunctionDeclaration) { - val propertyEdge = PropertyEdge(this, realizedFunction) - propertyEdge.addProperty(Properties.INDEX, realizationEdges.size) - realizationEdges.add(propertyEdge) + realizationEdges.add(realizedFunction) } fun removeRealization(realizedFunction: FunctionDeclaration?) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt index 9e2eb140bc..630cd3a77c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt @@ -26,10 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -38,11 +37,13 @@ import org.neo4j.ogm.annotation.Relationship class IncludeDeclaration : Declaration() { @Relationship(value = "INCLUDES", direction = Relationship.Direction.OUTGOING) @AST - private val includeEdges: MutableList> = ArrayList() + private val includeEdges = astEdgesOf() + val includes: List by PropertyEdgeDelegate(IncludeDeclaration::includeEdges) @Relationship(value = "PROBLEMS", direction = Relationship.Direction.OUTGOING) @AST - private val problemEdges: MutableList> = ArrayList() + private val problemEdges = astEdgesOf() + val problems: List by PropertyEdgeDelegate(IncludeDeclaration::problemEdges) /** * This property refers to the file or directory or path. For example, in C this refers to an @@ -50,15 +51,9 @@ class IncludeDeclaration : Declaration() { */ var filename: String? = null - val includes: List by PropertyEdgeDelegate(IncludeDeclaration::includeEdges) - - val problems: List by PropertyEdgeDelegate(IncludeDeclaration::problemEdges) - fun addInclude(includeDeclaration: IncludeDeclaration?) { if (includeDeclaration == null) return - val propertyEdge = PropertyEdge(this, includeDeclaration) - propertyEdge.addProperty(Properties.INDEX, includeEdges.size) - includeEdges.add(propertyEdge) + includeEdges.add(includeDeclaration) } fun addProblems(c: Collection) { @@ -68,9 +63,7 @@ class IncludeDeclaration : Declaration() { } fun addProblem(problemDeclaration: ProblemDeclaration) { - val propertyEdge = PropertyEdge(this, problemDeclaration) - propertyEdge.addProperty(Properties.INDEX, problemEdges.size) - problemEdges.add(propertyEdge) + problemEdges.add(problemDeclaration) } override fun toString(): String { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt index ecee983b6f..6f1f0d4cfb 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt @@ -26,8 +26,8 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.Statement import java.util.Objects import org.neo4j.ogm.annotation.Relationship @@ -53,7 +53,7 @@ class NamespaceDeclaration : Declaration(), DeclarationHolder, StatementHolder, /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) @AST - override var statementEdges: MutableList> = ArrayList() + override var statementEdges = astEdgesOf() /** * In some languages, there is a relationship between paths / directories and the package @@ -73,8 +73,7 @@ class NamespaceDeclaration : Declaration(), DeclarationHolder, StatementHolder, addIfNotContains(declarations, declaration) } - override var statements: List by - PropertyEdgeDelegate(NamespaceDeclaration::statementEdges) + override var statements by PropertyEdgeDelegate(NamespaceDeclaration::statementEdges) override val eogStarters: List get() { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt index 31ead94be0..90d86619a5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt @@ -26,9 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.types.DeclaresType import de.fraunhofer.aisec.cpg.graph.types.ObjectType @@ -45,39 +45,33 @@ open class RecordDeclaration : @Relationship(value = "FIELDS", direction = Relationship.Direction.OUTGOING) @AST - var fieldEdges: MutableList> = ArrayList() - + var fieldEdges = astEdgesOf() var fields by PropertyEdgeDelegate(RecordDeclaration::fieldEdges) @Relationship(value = "METHODS", direction = Relationship.Direction.OUTGOING) @AST - var methodEdges: MutableList> = ArrayList() - + var methodEdges = astEdgesOf() var methods by PropertyEdgeDelegate(RecordDeclaration::methodEdges) @Relationship(value = "CONSTRUCTORS", direction = Relationship.Direction.OUTGOING) @AST - var constructorEdges: MutableList> = ArrayList() - + var constructorEdges = astEdgesOf() var constructors by PropertyEdgeDelegate(RecordDeclaration::constructorEdges) @Relationship(value = "RECORDS", direction = Relationship.Direction.OUTGOING) @AST - var recordEdges: MutableList> = ArrayList() - + var recordEdges = astEdgesOf() var records by PropertyEdgeDelegate(RecordDeclaration::recordEdges) @Relationship(value = "TEMPLATES", direction = Relationship.Direction.OUTGOING) @AST - var templateEdges: MutableList> = ArrayList() - + var templateEdges = astEdgesOf() var templates by PropertyEdgeDelegate(RecordDeclaration::templateEdges) /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) @AST - override var statementEdges: MutableList> = ArrayList() - + override var statementEdges = astEdgesOf() override var statements by PropertyEdgeDelegate(RecordDeclaration::statementEdges) @Transient var superClasses: MutableList = ArrayList() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt index 4a50f04d19..08e0810a2b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt @@ -26,12 +26,10 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import java.util.* -import kotlin.collections.ArrayList import org.neo4j.ogm.annotation.Relationship /** Node representing a declaration of a template class or struct */ @@ -43,15 +41,11 @@ class RecordTemplateDeclaration : TemplateDeclaration() { */ @Relationship(value = "REALIZATION", direction = Relationship.Direction.OUTGOING) @AST - val realizationEdges: MutableList> = ArrayList() - - override val realizations: List by - PropertyEdgeDelegate(RecordTemplateDeclaration::realizationEdges) + val realizationEdges = astEdgesOf() + override val realizations by PropertyEdgeDelegate(RecordTemplateDeclaration::realizationEdges) fun addRealization(realizedRecord: RecordDeclaration) { - val propertyEdge = PropertyEdge(this, realizedRecord) - propertyEdge.addProperty(Properties.INDEX, realizationEdges.size) - realizationEdges.add(propertyEdge) + realizationEdges.add(realizedRecord) } fun removeRealization(realizedRecordDeclaration: RecordDeclaration) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt index 94481cf24b..e0374d902f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt @@ -28,10 +28,9 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.DeclarationHolder import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import org.neo4j.ogm.annotation.NodeEntity import org.neo4j.ogm.annotation.Relationship @@ -54,8 +53,7 @@ abstract class TemplateDeclaration : Declaration(), DeclarationHolder { /** Parameters the Template requires for instantiation */ @Relationship(value = "PARAMETERS", direction = Relationship.Direction.OUTGOING) @AST - var parameterEdges: MutableList> = ArrayList() - + var parameterEdges = astEdgesOf() val parameters by PropertyEdgeDelegate(TemplateDeclaration::parameterEdges) val parametersWithDefaults: List @@ -86,15 +84,11 @@ abstract class TemplateDeclaration : Declaration(), DeclarationHolder { } fun addParameter(parameterizedType: TypeParameterDeclaration) { - val propertyEdge = PropertyEdge(this, parameterizedType) - propertyEdge.addProperty(Properties.INDEX, parameterEdges.size) - parameterEdges.add(propertyEdge) + parameterEdges.add(parameterizedType) } fun addParameter(nonTypeTemplateParamDeclaration: ParameterDeclaration) { - val propertyEdge = PropertyEdge(this, nonTypeTemplateParamDeclaration) - propertyEdge.addProperty(Properties.INDEX, parameterEdges.size) - parameterEdges.add(propertyEdge) + parameterEdges.add(nonTypeTemplateParamDeclaration) } override val declarations: List diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt index f916ce71e0..86ca0cd2d5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt @@ -26,10 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.Statement import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder @@ -41,34 +40,26 @@ class TranslationUnitDeclaration : /** A list of declarations within this unit. */ @Relationship(value = "DECLARATIONS", direction = Relationship.Direction.OUTGOING) @AST - val declarationEdges: MutableList> = ArrayList() + val declarationEdges = astEdgesOf() + override val declarations by PropertyEdgeDelegate(TranslationUnitDeclaration::declarationEdges) /** A list of includes within this unit. */ @Relationship(value = "INCLUDES", direction = Relationship.Direction.OUTGOING) @AST - val includeEdges: MutableList> = ArrayList() + val includeEdges = astEdgesOf() + val includes by PropertyEdgeDelegate(TranslationUnitDeclaration::includeEdges) /** A list of namespaces within this unit. */ @Relationship(value = "NAMESPACES", direction = Relationship.Direction.OUTGOING) @AST - val namespaceEdges: MutableList> = ArrayList() + val namespaceEdges = astEdgesOf() + val namespaces by PropertyEdgeDelegate(TranslationUnitDeclaration::namespaceEdges) /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) @AST - override var statementEdges: MutableList> = ArrayList() - - override val declarations: List - get() = unwrap(declarationEdges) - - override var statements: List by - PropertyEdgeDelegate(TranslationUnitDeclaration::statementEdges) - - val includes: List - get() = unwrap(includeEdges) - - val namespaces: List - get() = unwrap(namespaceEdges) + override var statementEdges = astEdgesOf() + override var statements by PropertyEdgeDelegate(TranslationUnitDeclaration::statementEdges) override fun addDeclaration(declaration: Declaration) { if (declaration is IncludeDeclaration) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt index 8d70aaa83d..5e5b4af55f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt @@ -28,14 +28,12 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.PopulatedByPass import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.flows.Usages import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.helpers.identitySetOf import de.fraunhofer.aisec.cpg.passes.SymbolResolver -import java.util.stream.Collectors import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.NodeEntity import org.neo4j.ogm.annotation.Relationship @@ -92,31 +90,11 @@ abstract class ValueDeclaration : Declaration(), HasType, HasAliases { */ @PopulatedByPass(SymbolResolver::class) @Relationship(value = "USAGE") - var usageEdges: MutableList> = ArrayList() + var usageEdges = Usages(this) /** All usages of the variable/field. */ @PopulatedByPass(SymbolResolver::class) - var usages: List - get() = unwrap(usageEdges, true) - /** Set all usages of the variable/field and assembles the access properties. */ - set(usages) { - usageEdges = - usages - .stream() - .map { ref: Reference -> - val edge = PropertyEdge(this, ref) - edge.addProperty(Properties.ACCESS, ref.access) - edge - } - .collect(Collectors.toList()) - } - - /** Adds a usage of the variable/field and assembles the access property. */ - fun addUsage(reference: Reference) { - val usageEdge = PropertyEdge(this, reference) - usageEdge.addProperty(Properties.ACCESS, reference.access) - usageEdges.add(usageEdge) - } + var usages by PropertyEdgeDelegate(ValueDeclaration::usageEdges) override fun toString(): String { return ToStringBuilder(this, TO_STRING_STYLE).appendSuper(super.toString()).toString() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Dataflow.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Dataflow.kt deleted file mode 100644 index c8418b0db8..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Dataflow.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.edge - -import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.declarations.Declaration -import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration -import de.fraunhofer.aisec.cpg.graph.declarations.TupleDeclaration -import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression -import org.neo4j.ogm.annotation.RelationshipEntity - -/** - * The granularity of the data-flow, e.g., whether the flow contains the whole object, or just a - * part of it, for example a record (class/struct) member. - * - * The helper functions [full] and [partial] can be used to construct either full or partial - * dataflow granularity. - */ -sealed interface Granularity - -/** - * This dataflow granularity is the default. The "whole" object is flowing from [Dataflow.start] to - * [Dataflow.end]. - */ -data object FullDataflowGranularity : Granularity - -/** - * This dataflow granularity denotes that not the "whole" object is flowing from [Dataflow.start] to - * [Dataflow.end] but only parts of it. Common examples include [MemberExpression] nodes, where we - * model a dataflow to the base, but only partially scoped to a particular field. - */ -class PartialDataflowGranularity( - /** The target that is affected by this partial dataflow. */ - val partialTarget: Declaration? -) : Granularity - -/** Creates a new [FullDataflowGranularity]. */ -fun full(): Granularity { - return FullDataflowGranularity -} - -/** Creates a new default [Granularity]. Currently, this defaults to [FullDataflowGranularity]. */ -fun default() = full() - -/** - * Creates a new [PartialDataflowGranularity]. The [target] is the [Declaration] that is affected by - * the partial dataflow. Examples include a [FieldDeclaration] for a [MemberExpression] or a - * [VariableDeclaration] for a [TupleDeclaration]. - */ -fun partial(target: Declaration?): PartialDataflowGranularity { - return PartialDataflowGranularity(target) -} - -/** - * This edge class defines a flow of data between [start] and [end]. The flow can have a certain - * [granularity]. - */ -@RelationshipEntity -open class Dataflow( - start: Node, - end: Node, - /** The granularity of this dataflow. */ - val granularity: Granularity = default() -) : PropertyEdge(start, end) { - override val label: String = "DFG" -} - -sealed interface CallingContext - -class CallingContextIn( - /** The call expression that affects this dataflow edge. */ - val call: CallExpression -) : CallingContext - -class CallingContextOut( - /** The call expression that affects this dataflow edge. */ - val call: CallExpression -) : CallingContext - -/** - * This edge class defines a flow of data between [start] and [end]. The flow must have a - * [callingContext] which allows for a context-sensitive dataflow analysis. This edge can also have - * a certain [granularity]. - */ -@RelationshipEntity -class ContextSensitiveDataflow( - start: Node, - end: Node, - /** The calling context affecting this dataflow. */ - val callingContext: CallingContext, - /** The granularity of this dataflow. */ - granularity: Granularity, -) : Dataflow(start, end, granularity) { - override val label: String = "DFG" -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Extensions.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Extensions.kt new file mode 100644 index 0000000000..7a568444b7 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Extensions.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +@file:Suppress("UNCHECKED_CAST") + +package de.fraunhofer.aisec.cpg.graph.edge + +import de.fraunhofer.aisec.cpg.graph.Node + +/** + * If the node list is backed by a [PropertyEdgeList] container (such as + * [de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdges]), this returns the first [PropertyEdge] that + * fulfills the [predicate]. + */ +fun List.edge(predicate: (PropertyEdge) -> Boolean): PropertyEdge? { + // This only works for unwrapped property edges + if (this !is UnwrappedPropertyEdgeList<*>) { + return null + } + + var edges = this.edges as PropertyEdgeList> + return edges.firstOrNull(predicate) +} + +/** + * If the node list is backed by a [PropertyEdgeList] container (such as + * [de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdges]), this returns all [PropertyEdge] edges that + * fulfill the [predicate]. + */ +fun List.edges(predicate: (PropertyEdge) -> Boolean): List> { + // This only works for unwrapped property edges + if (this !is UnwrappedPropertyEdgeList<*>) { + return listOf() + } + + var edges = this.edges as PropertyEdgeList> + return edges.filter(predicate) +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Properties.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Properties.kt deleted file mode 100644 index ed8f2063ae..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/Properties.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.edge - -import java.util.* - -/** - * INDEX: (int) Indicates the position in a list of edges - * - * BRANCH: (boolean) If we have multiple EOG edges the branch property indicates which EOG edge - * leads to true branch (expression evaluated to true) or the false branch (e.g. with an if/else - * condition) - * - * DEFAULT: (boolean) Indicates which arguments edge of a CallExpression leads to a default argument - * - * NAME: (String) An optional name for the property edge - * - * [UNREACHABLE]:(boolean) True if the edge flows into unreachable code i.e. a branch condition - * which is always false. - * - * [DEPENDENCE]: ([DependenceType] Specifies the type of dependence the property edge might - * represent - */ -enum class Properties { - INDEX, - BRANCH, - NAME, - INSTANTIATION, - UNREACHABLE, - ACCESS, - DEPENDENCE, - DYNAMIC_INVOKE, - SENSITIVITY, - CALLING_CONTEXT_IN, - CALLING_CONTEXT_OUT, -} - -/** The types of dependencies that might be represented in the CPG */ -enum class DependenceType { - CONTROL, - DATA -} - -/** Sensitivity options (of DFG edges). */ -enum class SensitivitySpecifier { - FIELD, - CONTEXT; - - infix fun and(other: SensitivitySpecifier) = Sensitivities.of(this, other) -} - -typealias Sensitivities = EnumSet - -infix fun Sensitivities.allOf(other: Sensitivities) = this.containsAll(other) - -infix fun Sensitivities.and(other: SensitivitySpecifier) = - Sensitivities.of(other, *this.toTypedArray()) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt index f9fb9408f7..664922f135 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt @@ -25,21 +25,14 @@ */ package de.fraunhofer.aisec.cpg.graph.edge +import com.fasterxml.jackson.annotation.JsonBackReference +import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.Node.Companion.TO_STRING_STYLE import de.fraunhofer.aisec.cpg.graph.Persistable -import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal -import java.lang.reflect.Field -import java.lang.reflect.InvocationTargetException -import java.lang.reflect.ParameterizedType import java.util.* -import kotlin.reflect.KMutableProperty1 -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty1 -import kotlin.reflect.jvm.isAccessible +import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.* -import org.neo4j.ogm.annotation.typeconversion.Convert -import org.slf4j.LoggerFactory /** * This class represents an edge between two [Node] objects in a Neo4J graph. It can be used to @@ -54,271 +47,76 @@ import org.slf4j.LoggerFactory * ``` */ @RelationshipEntity -open class PropertyEdge : Persistable { +abstract class PropertyEdge : Persistable, Cloneable { /** Required field for object graph mapping. It contains the node id. */ @field:Id @field:GeneratedValue private val id: Long? = null // Node where the edge is outgoing - @field:StartNode var start: Node + @JsonIgnore @field:StartNode var start: Node // Node where the edge is ingoing - @field:EndNode var end: T + @JsonBackReference @field:EndNode var end: T constructor(start: Node, end: T) { this.start = start this.end = end - properties = EnumMap(Properties::class.java) } constructor(propertyEdge: PropertyEdge) { start = propertyEdge.start end = propertyEdge.end - properties = EnumMap(Properties::class.java) - properties.putAll(propertyEdge.properties) } - constructor(start: Node, end: T, properties: MutableMap) { - this.start = start - this.end = end - this.properties = properties - } - - open val label: String = "EDGE" - - /** Map containing all properties of an edge */ - @Convert(PropertyEdgeConverter::class) private var properties: MutableMap + @Transient open val label: String = "EDGE" - fun getProperty(property: Properties): Any? { - return properties.getOrDefault(property, null) - } - - /** - * Adds a property to a [PropertyEdge] If the object is not a built-in type you must provide a - * serializer and deserializer in the [PropertyEdgeConverter] - * - * @param property String containing the name of the property - * @param value Object containing the value of the property - */ - fun addProperty(property: Properties, value: Any?) { - properties[property] = value - } + /** The index of this node, if it is stored in an [PropertyEdgeList]. */ + var index: Int? = null - fun addProperties(propertyMap: Map?) { - propertyMap?.let { properties.putAll(it) } - } + /** An optional name. */ + var name: String? = null override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is PropertyEdge<*>) return false - return (properties == other.properties && start == other.start && end == other.end) + return (start == other.start && end == other.end) } fun propertyEquals(obj: Any?): Boolean { if (this === obj) return true if (obj !is PropertyEdge<*>) return false - return properties == obj.properties - } - - /** - * Checks if the properties of the edge contain the given properties with the specified values. - */ - fun containsProperties(props: Map): Boolean { - return properties.entries.containsAll(props.entries) + return true } override fun hashCode(): Int { - return Objects.hash(end, properties) + return Objects.hash(start, end) } - companion object { - protected val log = LoggerFactory.getLogger(PropertyEdge::class.java) - - fun ?> findPropertyEdgesByPredicate( - edges: Collection, - predicate: (S) -> Boolean - ): List { - return edges.filter(predicate) - } - - /** - * Add/Update index element of list of PropertyEdges - * - * @param propertyEdges propertyEdge list - * @return new PropertyEdge list with updated index property - */ - fun applyIndexProperty( - propertyEdges: List> - ): List> { - for ((counter, propertyEdge) in propertyEdges.withIndex()) { - propertyEdge.addProperty(Properties.INDEX, counter) - } - return propertyEdges - } - - /** - * Transforms a List of Nodes into targets of PropertyEdges. Include Index Property as Lists - * are indexed - * - * @param nodes List of nodes that should be transformed into PropertyEdges - * @param commonRelationshipNode node where all the Edges should start - * @return List of PropertyEdges with the targets of the nodes and index property. - */ - @JvmStatic - fun wrap( - nodes: List, - commonRelationshipNode: Node, - outgoing: Boolean = true - ): MutableList> { - val propertyEdges: MutableList> = ArrayList() - for (n in nodes) { - val propertyEdge = - if (outgoing) { - PropertyEdge(commonRelationshipNode, n) - } else { - PropertyEdge(n, commonRelationshipNode) - } - propertyEdge.addProperty(Properties.INDEX, propertyEdges.size) - propertyEdges.add(propertyEdge as PropertyEdge) - } - return propertyEdges - } - - /** - * Unwraps this property edge into a list of its target nodes. - * - * @param collection the collection of edges - * @param outgoing whether it is outgoing or not - * @param the type of the edges - * @return the list of target nodes - */ - @JvmStatic - @JvmOverloads - fun unwrap( - collection: List>, - outgoing: Boolean = true - ): List { - return collection.map { if (outgoing) it.end else it.start as T } - } - - /** - * @param collection is a collection that presumably holds property edges - * @param outgoing direction of the edges - * @return collection of nodes containing the targets of the edges - */ - private fun unwrapPropertyEdgeCollection( - collection: Collection<*>, - outgoing: Boolean - ): Any { - var element: Any? = null - val value = collection.stream().findAny() - if (value.isPresent) { - element = value.get() - } - if (element is PropertyEdge<*>) { - try { - val outputCollection = - collection.javaClass - .getDeclaredConstructor() - .newInstance() - .filterIsInstance() - .toMutableList() - for (obj in collection) { - if (obj is PropertyEdge<*>) { - if (outgoing) { - outputCollection.add(obj.end) - } else { - outputCollection.add(obj.start) - } - } - } - return outputCollection - } catch (e: InstantiationException) { - log.warn("PropertyEdges could not be unwrapped") - } catch (e: IllegalAccessException) { - log.warn("PropertyEdges could not be unwrapped") - } catch (e: InvocationTargetException) { - log.warn("PropertyEdges could not be unwrapped") - } catch (e: NoSuchMethodException) { - log.warn("PropertyEdges could not be unwrapped") - } - } - return collection - } - - /** - * @param obj PropertyEdge or collection of property edges that must be unwrapped - * @param outgoing direction of the edge - * @return node or collection representing target of edge - */ - @JvmStatic - fun unwrapPropertyEdge(obj: Any, outgoing: Boolean): Any { - if (obj is PropertyEdge<*>) { - return if (outgoing) { - obj.end - } else { - obj.start - } - } else if (obj is Collection<*> && obj.isNotEmpty()) { - return unwrapPropertyEdgeCollection(obj, outgoing) - } - return obj - } - - /** - * Checks if an Object is a PropertyEdge or a collection of PropertyEdges - * - * @param f Field containing the object - * @param obj object that is checked if it is a PropertyEdge - * @return true if obj is/contains a PropertyEdge - */ - @JvmStatic - fun checkForPropertyEdge(f: Field, obj: Any?): Boolean { - if (obj is PropertyEdge<*>) { - return true - } else if (obj is Collection<*>) { - val collectionTypes = - listOf(*(f.genericType as ParameterizedType).actualTypeArguments) - for (t in collectionTypes) { - if (t is ParameterizedType) { - return t.rawType == PropertyEdge::class.java - } else if (PropertyEdge::class.java == t) { - return true - } - } - } - return false + /** + * This special hash-code function takes into account whether the caller put this edge into an + * outgoing or incoming edge. This is primarily used by [PropertyEdgeList.hashCode]. + */ + fun directionalHashCode(outgoing: Boolean = true): Int { + return if (outgoing) { + Objects.hash(end) + } else { + Objects.hash(start) } + } - fun checkForPropertyEdge(member: KProperty1, obj: Any?): Boolean { - if (obj is PropertyEdge<*>) { - return true - } else if (obj is Collection<*>) { - val returnType = member.returnType - return returnType.classifier == List::class && - returnType.arguments.any { it.type?.classifier == PropertyEdge::class } - } - return false - } + override fun toString(): String { + return ToStringBuilder(this, TO_STRING_STYLE) + .appendSuper(super.toString()) + .append("end", end) + .toString() + } - @JvmStatic - fun removeElementFromList( - propertyEdges: List>, - element: T, - end: Boolean - ): List> { - val newPropertyEdges: MutableList> = ArrayList() - for (propertyEdge in propertyEdges) { - if (end && propertyEdge.end != element) { - newPropertyEdges.add(propertyEdge) - } - if (!end && propertyEdge.start != element) { - newPropertyEdges.add(propertyEdge) - } - } - return applyIndexProperty(newPropertyEdges) - } + public override fun clone(): PropertyEdge { + // needs to be implemented by sub-classes + return super.clone() as PropertyEdge + } + companion object { @JvmStatic fun propertyEqualsList( propertyEdges: List>?, @@ -344,62 +142,3 @@ open class PropertyEdge : Persistable { } } } - -/** - * This class can be used to implement - * [delegated properties](https://kotlinlang.org/docs/delegated-properties.html) in [Node] classes. - * The most common use case is to have a property that is a list of [PropertyEdge] objects (for - * persistence) and a second (delegated) property that allows easy access just to the connected - * nodes of the individual edges for in-memory access. - * - * For example: - * ```kotlin - * - * class MyNode { - * @Relationship(value = "EXPRESSIONS", direction = "OUTGOING") - * @field:SubGraph("AST") - * var expressionsEdges = mutableListOf>() - * var expressions by PropertyEdgeDelegate(MyNode::expressionsEdges) - * } - * ``` - * - * This class is intentionally marked with [Transient], so that the delegated properties are not - * transferred to the Neo4J OGM. Only the property that contains the property edges should be - * persisted in the graph database. - */ -@Transient -class PropertyEdgeDelegate( - val edge: KProperty1>>, - val outgoing: Boolean = true -) { - operator fun getValue(thisRef: S, property: KProperty<*>): List { - return PropertyEdge.unwrap(edge.get(thisRef), outgoing) - } - - operator fun setValue(thisRef: S, property: KProperty<*>, value: List) { - if (edge is KMutableProperty1) { - val callable = edge.setter - callable.isAccessible = true - edge.setter.call(thisRef, PropertyEdge.wrap(value, thisRef as Node, outgoing)) - } - } -} - -/** Similar to a [PropertyEdgeDelegate], but with a [Set] instead of [List]. */ -@Transient -class PropertyEdgeSetDelegate( - val edge: KProperty1>>, - val outgoing: Boolean = true -) { - operator fun getValue(thisRef: S, property: KProperty<*>): MutableSet { - return PropertyEdge.unwrap(edge.get(thisRef).toList(), outgoing).toMutableSet() - } - - operator fun setValue(thisRef: S, property: KProperty<*>, value: MutableSet) { - if (edge is KMutableProperty1) { - val callable = edge.setter - callable.isAccessible = true - edge.setter.call(thisRef, PropertyEdge.wrap(value.toList(), thisRef as Node, outgoing)) - } - } -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverter.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverter.kt deleted file mode 100644 index cd66858896..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverter.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.edge - -import java.util.* -import java.util.function.Function -import org.neo4j.ogm.typeconversion.CompositeAttributeConverter - -class PropertyEdgeConverter : CompositeAttributeConverter> { - /** - * For every PropertyValue that is not a supported type, a serializer and a deserializer must be - * provided Supported Types: - * - * PRIMITIVES = - * char,byte,short,int,long,float,double,boolean,char[],byte[],short[],int[],long[],float[],double[],boolean[] - * AUTOBOXERS = java.lang.Object, java.lang.Character, java.lang.Byte, java.lang.Short, - * java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Boolean, - * java.lang.String, java.lang.Object[], java.lang.Character[], java.lang.Byte[], - * java.lang.Short[], java.lang.Integer[], java.lang.Long[], java.lang.Float[], - * java.lang.Double[], java.lang.Boolean[], java.lang.String[] - */ - // Maps a class to a function that serialized the object from the given class - private val serializer: Map> - - // Maps a string (key of the property) to a function that deserializes the property - private val deserializer: Map> - - init { - serializer = PropertyEdgeConverterManager.instance.serializer - deserializer = PropertyEdgeConverterManager.instance.deserializer - } - - override fun toGraphProperties(value: Map): Map { - val result: MutableMap = HashMap() - for ((key, propertyValue) in value) { - if (propertyValue != null && serializer.containsKey(propertyValue.javaClass.name)) { - result[key.name] = serializer[propertyValue.javaClass.name]?.apply(propertyValue) - } else { - result[key.name] = propertyValue - } - } - return result - } - - override fun toEntityAttribute(value: Map): Map { - val result: MutableMap = EnumMap(Properties::class.java) - for (prop in Properties.entries) { - if (deserializer.containsKey(prop.name)) { - val deserializedProperty = - value[prop.name]?.let { deserializer[prop.name]?.apply(it) } - result[prop] = deserializedProperty - } else { - result[prop] = value[prop.name] - } - } - return result - } -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverterManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverterManager.kt deleted file mode 100644 index 6a044bc58a..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeConverterManager.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.edge - -import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration.TemplateInitialization -import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression -import java.util.function.Function - -/** - * Since Neo4J uses the PropertyEdgeConverter (as it implements the CompositeAttributeConverter - * interface), we cannot define it as a singleton, as it requires to have a constructor. We want to - * be able to dynamically define converters for PropertyEdges that have more complex structures such - * as enums or custom classes, we need a singleton to be able to add the converters. Refer to the - * documentation of [PropertyEdgeConverter] to see which primitives are supported by default, and - * which require a custom converter. - */ -class PropertyEdgeConverterManager private constructor() { - // Maps a class to a function that serialized the object from the given class - val serializer: MutableMap> = HashMap() - - // Maps a string (key of the property) to a function that deserializes the property - val deserializer: MutableMap> = HashMap() - - init { - // Add here converters for PropertyEdges - addSerializer(TemplateInitialization::class.java.name) { obj: Any -> obj.toString() } - addDeserializer("INSTANTIATION") { s: Any? -> - if (s != null) TemplateInitialization.valueOf(s.toString()) else null - } - addSerializer(CallExpression::class.java.name) { it.toString() } - addDeserializer("CALLING_CONTEXT_IN") { null } // TODO: Not supported yet - addDeserializer("CALLING_CONTEXT_OUT") { null } // TODO: Not supported yet - } - - fun addSerializer(clazz: String, func: Function) { - serializer[clazz] = func - } - - fun addDeserializer(name: String, func: Function) { - deserializer[name] = func - } - - companion object { - val instance = PropertyEdgeConverterManager() - } -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeList.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeList.kt new file mode 100644 index 0000000000..5e8205cda6 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgeList.kt @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge + +import de.fraunhofer.aisec.cpg.graph.Node +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 +import org.neo4j.ogm.annotation.* + +/** + * This interfaces is an extension of [MutableCollection] that holds specific functions for the + * collection of [PropertyEdge] edges. + */ +interface PropertyEdgeCollection< + NodeType : Node, + PropertyEdgeType : PropertyEdge, +> : MutableCollection { + var thisRef: Node + var init: (start: Node, end: NodeType) -> PropertyEdgeType + var outgoing: Boolean + + /** + * Creates a new edge with the target node and an optional [builder] to include edge properties. + * If [outgoing] is true, the edge is created from [thisRef] -> [target], otherwise from + * [target] to [thisRef]. + */ + @Suppress("UNCHECKED_CAST") + fun add(target: NodeType, builder: (PropertyEdgeType.() -> Unit)? = null): Boolean { + val edge = + if (outgoing) { + init(thisRef, target) + } else { + init(target, thisRef as NodeType) + } + + // Apply builder + if (builder != null) { + builder(edge) + } + + // Add it + return this.add(edge) + } + + operator fun plusAssign(end: NodeType) { + add(end) + } + + /** Clears the collection and adds the [nodes]. */ + fun resetTo(nodes: Collection) { + clear() + for (n in nodes) { + this += n + } + } + + /** + * Converts this collection of edges into a collection of nodes for easier access to the + * "target" nodes. + */ + fun unwrap(outgoing: Boolean = true): MutableCollection + + /** + * This function will be exectuted after the edge was added to the container. This can be used + * to propagate the edge to other properties or register additional handlers, e.g. a + * [TypeObserver]. + */ + fun handlePostAdd(edge: PropertyEdgeType) +} + +/** A helper function for [PropertyEdgeCollection.unwrap]. */ +@Suppress("UNCHECKED_CAST") +private fun > internalUnwrap( + edges: PropertyEdgeCollection>, + outgoing: Boolean = true, + createUnwrapper: + (PropertyEdgeCollection>) -> CollectionType +): CollectionType { + var unwrapped = createUnwrapper(edges) + edges.mapTo(unwrapped) { if (outgoing) it.end else it.start as NodeType } + return unwrapped +} + +/** + * This is a special hashcode implementation that takes into account whether this edge container is + * containing incoming or outgoing edges. It builds the hash-code based on + * [PropertyEdge.directionalHashCode]. The reason for this is to avoid loops in the hash-code + * implementation. + */ +private fun internalHashcode( + edges: PropertyEdgeCollection>, + outgoing: Boolean = true, +): Int { + var hashCode = 0 + + for (edge in edges) { + hashCode = 31 * hashCode + edge.directionalHashCode(outgoing) + } + + return hashCode +} + +/** + * This class extends a list of property edges. This allows us to use list of property edges more + * conveniently. + */ +abstract class PropertyEdgeList>( + override var thisRef: Node, + override var init: (start: Node, end: T) -> P, + override var outgoing: Boolean = true, + var postAdd: ((P) -> Unit)? = null, + var preRemove: ((P) -> Unit)? = null +) : ArrayList

(), PropertyEdgeCollection { + + override fun add(e: P): Boolean { + // Make sure, the index is always set + if (e.index == null) { + e.index = this.size + } + + val ok = super.add(e) + if (ok) { + handlePostAdd(e) + } + + return ok + } + + override fun add(index: Int, element: P) { + // Make sure, the index is always set + element.index = this.size + + super.add(index, element) + + // TODO: actually we need to re-compute all other index properties + + handlePostAdd(element) + } + + override fun unwrap(outgoing: Boolean): MutableList { + return internalUnwrap(this, outgoing, ::UnwrappedPropertyEdgeList) + } + + override fun equals(o: Any?): Boolean { + if (o !is PropertyEdgeList<*, *>) return false + + // Otherwise, try to compare the contents of the lists with the propertyEquals method + if (this.size == o.size) { + for (i in this.indices) { + if (this[i] != o[i]) { + return false + } + } + return true + } + + return false + } + + override fun hashCode(): Int { + return internalHashcode(this, outgoing) + } + + override fun handlePostAdd(edge: P) { + postAdd?.invoke(edge) + } +} + +/** + * This class extends a list of property edges. This allows us to use list of property edges more + * conveniently. + */ +abstract class PropertyEdgeSet>( + override var thisRef: Node, + override var init: (start: Node, end: T) -> P, + override var outgoing: Boolean = true, +) : HashSet

(), PropertyEdgeCollection { + override fun add(e: P): Boolean { + val ok = super.add(e) + if (ok) { + handlePostAdd(e) + } + + return ok + } + + override fun unwrap(outgoing: Boolean): MutableSet { + return internalUnwrap(this, outgoing, ::UnwrappedPropertyEdgeSet) + } + + override fun equals(o: Any?): Boolean { + if (this === o) return true + if (o !is PropertyEdgeSet<*, *>) return false + + // Otherwise, try to compare the contents of the lists with the propertyEquals method + return this.containsAll(o) + } + + override fun hashCode(): Int { + return internalHashcode(this, outgoing) + } +} + +/** + * This class can be used to implement + * [delegated properties](https://kotlinlang.org/docs/delegated-properties.html) in [Node] classes. + * The most common use case is to have a property that is a list of [PropertyEdge] objects (for + * persistence) and a second (delegated) property that allows easy access just to the connected + * nodes of the individual edges for in-memory access. + * + * For example: + * ```kotlin + * class MyNode { + * @Relationship(value = "EXPRESSIONS", direction = "OUTGOING") + * @AST + * var expressionsEdges = astChildrenOf() + * var expressions by PropertyEdgeDelegate(MyNode::expressionsEdges) + * } + * ``` + * + * This class is intentionally marked with [Transient], so that the delegated properties are not + * transferred to the Neo4J OGM. Only the property that contains the property edges should be + * persisted in the graph database. + */ +@Transient +class PropertyEdgeDelegate( + val edgeProperty: + KProperty1>>, + val outgoing: Boolean = true, +) { + operator fun getValue(thisRef: NodeType, property: KProperty<*>): List { + var list = edgeProperty.get(thisRef) + return list.unwrap(outgoing) + } + + operator fun setValue(thisRef: NodeType, property: KProperty<*>, value: List) { + var list = edgeProperty.get(thisRef) + if (edgeProperty is KMutableProperty1) { + list.resetTo(value) + } + } +} + +/** Similar to a [PropertyEdgeDelegate], but with a [Set] instead of [List]. */ +@Transient +class PropertyEdgeSetDelegate( + val edgeProperty: + KProperty1>>, + val outgoing: Boolean = true, +) { + operator fun getValue(thisRef: NodeType, property: KProperty<*>): Set { + var set = edgeProperty.get(thisRef) + return set.unwrap(outgoing) + } + + operator fun setValue(thisRef: NodeType, property: KProperty<*>, value: Set) { + var set = edgeProperty.get(thisRef) + if (edgeProperty is KMutableProperty1) { + set.resetTo(value) + } + } +} + +/** + * This interface can be used for edge collections, which "mirror" its content to another property. + * This can be used to automatically populate next/prev flow edges. + */ +interface MirroredEdgeCollection> { + var mirrorProperty: KProperty> + + /** + * Adds this particular edge to its [mirrorProperty]. We need the information if this is an + * [outgoing] or incoming edge collection. + */ + fun addToMirror(edge: PropertyEdgeType, outgoing: Boolean) { + // Handle our mirror property. We add some extra "in" checks here, otherwise things will + // loop. + if (outgoing) { + var prevOfNext = mirrorProperty.call(edge.end) + if (edge !in prevOfNext) { + prevOfNext.add(edge) + } + } else { + var nextOfPrev = mirrorProperty.call(edge.start) + if (edge !in nextOfPrev) { + nextOfPrev.add(edge) + } + } + } +} + +/** + * An internal container to access the "unwrapped" version of a [PropertyEdgeCollection] as a list. + * This can only be accessed through [PropertyEdgeList.unwrap]. + */ +internal class UnwrappedPropertyEdgeList< + NodeType : Node, +>( + /** The original edges container, which can be accessed through this unwrapper. */ + val edges: PropertyEdgeCollection> +) : ArrayList() + +/** + * An internal container to access the "unwrapped" version of a [PropertyEdgeCollection] as a list. + * This can only be accessed through [UnwrappedPropertyEdgeSet.unwrap]. + */ +internal class UnwrappedPropertyEdgeSet< + NodeType : Node, +>( + /** The original edges container, which can be accessed through this unwrapper. */ + val edges: PropertyEdgeCollection> +) : HashSet() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/AstEdge.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/AstEdge.kt new file mode 100644 index 0000000000..83fc3b089c --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/AstEdge.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.ast + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeList +import org.neo4j.ogm.annotation.* + +/** This property edge describes a parent/child relationship in the Abstract Syntax Tree (AST). */ +@RelationshipEntity +open class AstEdge : PropertyEdge { + constructor(start: Node, end: T) : super(start, end) { + // In a future PR, we will set the astParent here + } +} + +/** Creates an [AstEdges] container starting from this node. */ +context(Node) +fun astEdgesOf( + postAdd: ((AstEdge) -> Unit)? = null, + preRemove: ((AstEdge) -> Unit)? = null, +): AstEdges> { + return AstEdges(this@Node, postAdd, preRemove) +} + +/** This property edge list describes elements that are AST children of a node. */ +@Suppress("UNCHECKED_CAST") +open class AstEdges>( + thisRef: Node, + postAdd: ((PropertyEdgeType) -> Unit)? = null, + preRemove: ((PropertyEdgeType) -> Unit)? = null, + init: (start: Node, end: NodeType) -> PropertyEdgeType = { start, end -> + AstEdge(start, end) as PropertyEdgeType + } +) : + PropertyEdgeList( + thisRef = thisRef, + init = init, + postAdd = postAdd, + preRemove = preRemove, + ) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/TemplateParameter.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/TemplateParameter.kt new file mode 100644 index 0000000000..52f527a63a --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ast/TemplateParameter.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.ast + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration.TemplateInitialization +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression + +/** This edge represents a template parameter that is attached to a [CallExpression]. */ +class TemplateParameter( + start: Node, + end: NodeType, + var instantiation: TemplateInitialization? = TemplateInitialization.EXPLICIT, +) : AstEdge(start, end) {} + +/** A container for [TemplateParameter] edges. */ +class TemplateParameters(thisRef: Node) : + AstEdges>(thisRef, init = ::TemplateParameter) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependence.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependence.kt new file mode 100644 index 0000000000..d83f93a338 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependence.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edge.MirroredEdgeCollection +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeList +import kotlin.reflect.KProperty +import org.neo4j.ogm.annotation.RelationshipEntity + +/** + * An edge in a Control Dependence Graph (CDG). Denotes that the [start] node exercises control + * dependence on the [end] node. + */ +@RelationshipEntity +class ControlDependence( + start: Node, + end: Node, + /** A set of [EvaluationOrder.branch] values. */ + var branches: Set = setOf(), +) : PropertyEdge(start, end), ProgramDependence { + /** All control dependence edges exercise control dependence. */ + override var dependence: DependenceType? = DependenceType.CONTROL + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ControlDependence) return false + return this.branches == other.branches && super.equals(other) + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + branches.hashCode() + return result + } +} + +/** A container of [ControlDependence] edges. [NodeType] is necessary because of the Neo4J OGM. */ +class ControlDependences : + PropertyEdgeList, MirroredEdgeCollection { + + override var mirrorProperty: KProperty> + + constructor( + thisRef: Node, + mirrorProperty: KProperty>, + outgoing: Boolean + ) : super(thisRef = thisRef, init = ::ControlDependence, outgoing = outgoing) { + this.mirrorProperty = mirrorProperty + } + + override fun handlePostAdd(edge: ControlDependence) { + // Delegate it to MirroredEdgeCollection + super.addToMirror(edge, outgoing) + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Dataflow.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Dataflow.kt new file mode 100644 index 0000000000..e47be7c533 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Dataflow.kt @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import com.fasterxml.jackson.annotation.JsonIgnore +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.declarations.* +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeSet +import de.fraunhofer.aisec.cpg.graph.statements.expressions.* +import de.fraunhofer.aisec.cpg.graph.types.HasType +import kotlin.reflect.KProperty +import org.neo4j.ogm.annotation.* + +/** + * The granularity of the data-flow, e.g., whether the flow contains the whole object, or just a + * part of it, for example a record (class/struct) member. + * + * The helper functions [full] and [partial] can be used to construct either full or partial + * dataflow granularity. + */ +sealed interface Granularity + +/** + * This dataflow granularity is the default. The "whole" object is flowing from [Dataflow.start] to + * [Dataflow.end]. + */ +data object FullDataflowGranularity : Granularity + +/** + * This dataflow granularity denotes that not the "whole" object is flowing from [Dataflow.start] to + * [Dataflow.end] but only parts of it. Common examples include [MemberExpression] nodes, where we + * model a dataflow to the base, but only partially scoped to a particular field. + */ +class PartialDataflowGranularity( + /** The target that is affected by this partial dataflow. */ + val partialTarget: Declaration? +) : Granularity + +/** Creates a new [FullDataflowGranularity]. */ +fun full(): Granularity { + return FullDataflowGranularity +} + +/** Creates a new default [Granularity]. Currently, this defaults to [FullDataflowGranularity]. */ +fun default() = full() + +/** + * Creates a new [PartialDataflowGranularity]. The [target] is the [Declaration] that is affected by + * the partial dataflow. Examples include a [FieldDeclaration] for a [MemberExpression] or a + * [VariableDeclaration] for a [TupleDeclaration]. + */ +fun partial(target: Declaration?): PartialDataflowGranularity { + return PartialDataflowGranularity(target) +} + +/** + * This edge class defines a flow of data between [start] and [end]. The flow can have a certain + * [granularity]. + */ +@RelationshipEntity +open class Dataflow( + start: Node, + end: Node, + /** The granularity of this dataflow. */ + @Transient @JsonIgnore val granularity: Granularity = default() +) : PropertyEdge(start, end), ProgramDependence { + override val label: String = "DFG" + + /** + * Whether a [Dataflow] edge has a [dependence] of [DependenceType.DATA] is decided in the + * [ProgramDependenceGraphPass]. + */ + override var dependence: DependenceType? = null + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Dataflow) return false + return this.granularity == other.granularity && super.equals(other) + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + granularity.hashCode() + return result + } +} + +sealed interface CallingContext + +class CallingContextIn( + /** The call expression that affects this dataflow edge. */ + val call: CallExpression +) : CallingContext + +class CallingContextOut( + /** The call expression that affects this dataflow edge. */ + val call: CallExpression +) : CallingContext + +/** + * This edge class defines a flow of data between [start] and [end]. The flow must have a + * [callingContext] which allows for a context-sensitive dataflow analysis. This edge can also have + * a certain [granularity]. + */ +@RelationshipEntity +class ContextSensitiveDataflow( + start: Node, + end: Node, + /** The calling context affecting this dataflow. */ + @JsonIgnore val callingContext: CallingContext, + /** The granularity of this dataflow. */ + granularity: Granularity, +) : Dataflow(start, end, granularity) { + override val label: String = "DFG" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ContextSensitiveDataflow) return false + return this.callingContext == other.callingContext && super.equals(other) + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + callingContext.hashCode() + return result + } +} + +/** This class represents a container of [Dataflow] property edges in a [thisRef]. */ +class Dataflows( + thisRef: Node, + var mirrorProperty: KProperty>, + outgoing: Boolean, +) : PropertyEdgeSet(thisRef = thisRef, init = ::Dataflow, outgoing = outgoing) { + + /** + * Adds a [Dataflow] edge from/to (depending on [outgoing]) the node which contains this edge + * container to/from [node], with the given [Granularity]. + */ + fun add( + node: Node, + granularity: Granularity = default(), + callingContext: CallingContext? = null, + ) { + if (outgoing) { + addNextDFG(node, granularity, callingContext) + } else { + addPrevDFG(node, granularity, callingContext) + } + } + + /** Adds a [Dataflow] edge from this node to [next], with the given [Granularity]. */ + private fun addNextDFG( + next: Node, + granularity: Granularity = default(), + callingContext: CallingContext? = null, + ) { + val edge = + if (callingContext != null) { + ContextSensitiveDataflow(thisRef, next, callingContext, granularity) + } else { + Dataflow(thisRef, next, granularity) + } + this.add(edge) + } + + /** + * Adds a [Dataflow] edge from [prev] node to this node, with the given [Granularity] and + * [CallingContext]. + */ + private fun addPrevDFG( + prev: Node, + granularity: Granularity = default(), + callingContext: CallingContext? = null, + ) { + val edge = + if (callingContext != null) { + ContextSensitiveDataflow(prev, thisRef, callingContext, granularity) + } else { + Dataflow(prev, thisRef, granularity) + } + this.add(edge) + } + + /** + * This connects our dataflow to our "mirror" property. Meaning that if we add a node to + * nextDFG, we add our thisRef to the "prev" of "next" and vice-versa. + */ + override fun handlePostAdd(edge: Dataflow) { + if (outgoing) { + var prevOfNext = mirrorProperty.call(edge.end) + prevOfNext.add(edge) + } else { + var prev = edge.start + var nextOfPrev = mirrorProperty.call(prev) + nextOfPrev.add(edge) + + // We want to propagate assigned types all through the previous DFG nodes. Therefore, we + // modify the DFG adding function for references and add a type observer to the previous + // node (if it is not ourselves) + if (thisRef is Reference && prev != thisRef && prev is HasType) { + prev.registerTypeObserver(thisRef as HasType.TypeObserver) + } + } + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/EvaluationOrder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/EvaluationOrder.kt new file mode 100644 index 0000000000..b0298584e5 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/EvaluationOrder.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeList +import org.neo4j.ogm.annotation.RelationshipEntity + +@RelationshipEntity +class EvaluationOrder( + start: Node, + end: Node, + /** + * True, if the edge flows into unreachable code e.g.a branch condition which is always false. + */ + var unreachable: Boolean = false, + + /** + * If we have multiple EOG edges the branch property indicates which EOG edge leads to true + * branch (expression evaluated to true) or the false branch (e.g. with an if/else condition). + * Otherwise, this property is null. + */ + var branch: Boolean? = null +) : PropertyEdge(start, end) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is EvaluationOrder) return false + return this.unreachable == other.unreachable && + this.branch == other.branch && + super.equals(other) + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + unreachable.hashCode() + result = 31 * result + branch.hashCode() + return result + } +} + +/** + * Holds a container of [EvaluationOrder] edges. + * + * Note: We would not actually need the type parameter here, since all target nodes are of type + * [Node], but if we skip this parameter, the Neo4J exporter does not recognize this as a "list". + */ +class EvaluationOrders( + thisRef: Node, + outgoing: Boolean = true, +) : + PropertyEdgeList( + thisRef = thisRef, + init = ::EvaluationOrder, + outgoing = outgoing + ) { + + override fun handlePostAdd(edge: EvaluationOrder) { + // Nothing to do yet + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Invoke.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Invoke.kt new file mode 100644 index 0000000000..ad2decc08f --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Invoke.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeList +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression +import org.neo4j.ogm.annotation.RelationshipEntity + +/** This edge class denotes the invocation of a [FunctionDeclaration] by a [CallExpression]. */ +@RelationshipEntity +class Invoke( + start: Node, + end: FunctionDeclaration, + /** True, if this is a "dynamic" invoke, meaning tht the in */ + var dynamicInvoke: Boolean = false, +) : PropertyEdge(start, end) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Invoke) return false + return this.dynamicInvoke == other.dynamicInvoke && super.equals(other) + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + dynamicInvoke.hashCode() + return result + } +} + +/** A container for [Usage] edges. */ +class Invokes(thisRef: CallExpression) : + PropertyEdgeList(thisRef = thisRef, init = ::Invoke) { + override fun handlePostAdd(edge: Invoke) { + // TODO: Make thisRef generic :( + edge.end.registerTypeObserver(thisRef as CallExpression) + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependence.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependence.kt new file mode 100644 index 0000000000..26ef2e7498 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependence.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edge.MirroredEdgeCollection +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeSet +import kotlin.reflect.KProperty + +/** + * This edge can represent some kind of dependence (see [dependence]). This is special in a way that + * this is not an edge class, but only a interface with two known implementors: [Dataflow] and + * [ControlDependence]. + */ +sealed interface ProgramDependence { + + /** + * The type of dependence (e.g. control or data or none). This field is intentionally nullable, + * because not all [Dataflow] edges are selected. This selection is performed in the + * [ProgramDependenceGraphPass]. + */ + var dependence: DependenceType? +} + +/** The types of dependencies that might be represented in the CPG */ +enum class DependenceType { + CONTROL, + DATA +} + +/** + * A container of [PropertyEdge] edges that act as a program dependence graph (PDG). The canonical + * version of this lives in [Node.prevPDGEdges] / [Node.nextPDGEdges] and is populated by the + * [ProgramDependenceGraphPass]. + * + * After population, this collection will contain a direct combination of two other edge collections + * ([Dataflows] and [ControlDependences]). If we would only handle an in-memory graph, we could just + * store the edges in their original collection (e.g. DFG) as well as in the PDG. But the Neo4J OGM + * does not support this, so unfortunately, we need to clone the edges before inserting them into + * the collection. If we ever got rid of the Neo4J OGM we could potentially also remove the cloning. + */ +class ProgramDependences : + PropertyEdgeSet>, + MirroredEdgeCollection> { + override var mirrorProperty: KProperty>> + + constructor( + thisRef: Node, + mirrorProperty: KProperty>>, + outgoing: Boolean + ) : super( + thisRef, + init = { start, end -> + throw UnsupportedOperationException( + "This container only allows adding existing edges, but not creating new ones." + ) + }, + outgoing + ) { + this.mirrorProperty = mirrorProperty + } + + override fun add(e: PropertyEdge): Boolean { + // Clone the edge before inserting. See comment above for a detailed explanation. + var clonedEdge = e.clone() + return super.add(clonedEdge) + } + + override fun handlePostAdd(edge: PropertyEdge) { + super.addToMirror(edge, outgoing) + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Usage.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Usage.kt new file mode 100644 index 0000000000..92d8bb67c8 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/Usage.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.graph.AccessValues +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeList +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference +import org.neo4j.ogm.annotation.RelationshipEntity + +/** This edge class denotes the usage of a [ValueDeclaration] in a [Reference]. */ +@RelationshipEntity +class Usage( + start: Node, + end: Reference, + /** The type of access (read/write/readwrite) of this usage. */ + var access: AccessValues? = null, +) : PropertyEdge(start, end) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Usage) return false + return this.access == other.access && super.equals(other) + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + access.hashCode() + return result + } +} + +/** A container for [Usage] edges. */ +class Usages(thisRef: ValueDeclaration) : + PropertyEdgeList(thisRef = thisRef, init = ::Usage) { + override fun handlePostAdd(edge: Usage) { + edge.access = edge.end.access + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt index 5dc54d9509..2697481444 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt @@ -27,10 +27,9 @@ package de.fraunhofer.aisec.cpg.graph.statements import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.declarations.Declaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -48,18 +47,14 @@ open class DeclarationStatement : Statement() { */ @Relationship(value = "DECLARATIONS", direction = Relationship.Direction.OUTGOING) @AST - var declarationEdges: MutableList> = ArrayList() - + var declarationEdges = astEdgesOf() override var declarations by PropertyEdgeDelegate(DeclarationStatement::declarationEdges) var singleDeclaration: Declaration? get() = if (isSingleDeclaration()) declarationEdges[0].end else null set(value) { if (value == null) return - declarationEdges.clear() - val propertyEdge = PropertyEdge(this, value) - propertyEdge.addProperty(Properties.INDEX, 0) - declarationEdges.add(propertyEdge) + declarationEdges.resetTo(listOf(value)) } fun isSingleDeclaration(): Boolean { @@ -71,9 +66,7 @@ open class DeclarationStatement : Statement() { } fun addToPropertyEdgeDeclaration(declaration: Declaration) { - val propertyEdge = PropertyEdge(this, declaration) - propertyEdge.addProperty(Properties.INDEX, declarationEdges.size) - declarationEdges.add(propertyEdge) + declarationEdges.add(declaration) } override fun toString(): String { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt index eeb9e6ac23..8aec97d07a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt @@ -26,7 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.statements import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdges +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import java.util.Objects @@ -54,15 +56,15 @@ class ForEachStatement : Statement(), BranchingNode, StatementHolder { override val branchedBy: Node? get() = iterable - override var statementEdges: MutableList> + override var statementEdges: AstEdges> get() { - val statements = mutableListOf>() - variable?.let { statements.add(PropertyEdge(this, it)) } - iterable?.let { statements.add(PropertyEdge(this, it)) } - statement?.let { statements.add(PropertyEdge(this, it)) } + val statements = astEdgesOf() + variable?.let { statements.add(AstEdge(this, it)) } + iterable?.let { statements.add(AstEdge(this, it)) } + statement?.let { statements.add(AstEdge(this, it)) } return statements } - set(value) { + set(_) { // Nothing to do here } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt index 9a36428482..893de225a7 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt @@ -27,7 +27,9 @@ package de.fraunhofer.aisec.cpg.graph.statements import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.StatementHolder -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdges +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder @@ -50,10 +52,14 @@ class LabelStatement : Statement(), StatementHolder { .toString() } - override var statementEdges: MutableList> - get() = subStatement?.let { PropertyEdge.wrap(listOf(it), this) } ?: mutableListOf() + override var statementEdges: AstEdges> + get() { + var list = astEdgesOf() + subStatement?.let { list.resetTo(listOf(it)) } + return list + } set(value) { - subStatement = PropertyEdge.unwrap(value).firstOrNull() + subStatement = value.unwrap().firstOrNull() } override fun equals(other: Any?): Boolean { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt index e0b1d4bce4..1a4d33375f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt @@ -30,9 +30,9 @@ import de.fraunhofer.aisec.cpg.graph.DeclarationHolder import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import java.util.* import org.neo4j.ogm.annotation.NodeEntity import org.neo4j.ogm.annotation.Relationship @@ -51,7 +51,7 @@ abstract class Statement : Node(), DeclarationHolder { */ @Relationship(value = "LOCALS", direction = Relationship.Direction.OUTGOING) @AST - var localEdges = mutableListOf>() + var localEdges = astEdgesOf() /** Virtual property to access [localEdges] without property edges. */ var locals by PropertyEdgeDelegate(Statement::localEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt index d92011554d..174fbc740d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt @@ -26,9 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.statements import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import java.util.* import org.neo4j.ogm.annotation.Relationship @@ -37,8 +37,7 @@ import org.neo4j.ogm.annotation.Relationship class TryStatement : Statement() { @Relationship(value = "RESOURCES", direction = Relationship.Direction.OUTGOING) @AST - var resourceEdges = mutableListOf>() - + var resourceEdges = astEdgesOf() var resources by PropertyEdgeDelegate(TryStatement::resourceEdges) @AST var tryBlock: Block? = null @@ -47,8 +46,7 @@ class TryStatement : Statement() { @Relationship(value = "CATCH_CLAUSES", direction = Relationship.Direction.OUTGOING) @AST - var catchClauseEdges = mutableListOf>() - + var catchClauseEdges = astEdgesOf() var catchClauses by PropertyEdgeDelegate(TryStatement::catchClauseEdges) override fun equals(other: Any?): Boolean { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt index 6361e8441c..23354c3553 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt @@ -27,8 +27,8 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.StatementHolder -import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.Statement import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder @@ -42,7 +42,7 @@ open class Block : Expression(), StatementHolder { /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) @AST - override var statementEdges = mutableListOf>() + override var statementEdges = astEdgesOf() /** * This variable helps to differentiate between static and non static initializer blocks. Static diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt index 717ed8a6fd..28fc81f5a5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -30,10 +30,11 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration.TemplateInitialization import de.fraunhofer.aisec.cpg.graph.edge.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.wrap +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.TemplateParameters +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edge.flows.Invokes import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.passes.SymbolResolver import java.util.* @@ -52,7 +53,7 @@ open class CallExpression : */ @PopulatedByPass(SymbolResolver::class) @Relationship(value = "INVOKES", direction = Relationship.Direction.OUTGOING) - var invokeEdges = mutableListOf>() + var invokeEdges = Invokes(this) protected set /** @@ -60,26 +61,14 @@ open class CallExpression : * property edges. */ @PopulatedByPass(SymbolResolver::class) - var invokes: List - get(): List { - val targets: MutableList = ArrayList() - for (propertyEdge in invokeEdges) { - targets.add(propertyEdge.end) - } - return Collections.unmodifiableList(targets) - } - set(value) { - unwrap(invokeEdges).forEach { it.unregisterTypeObserver(this) } - invokeEdges = wrap(value, this) - value.forEach { it.registerTypeObserver(this) } - } + var invokes by PropertyEdgeDelegate(CallExpression::invokeEdges) /** * The list of arguments of this call expression, backed by a list of [PropertyEdge] objects. */ @Relationship(value = "ARGUMENTS", direction = Relationship.Direction.OUTGOING) @AST - var argumentEdges = mutableListOf>() + var argumentEdges = astEdgesOf() /** * The list of arguments as a simple list. This is a delegated property delegated to @@ -131,12 +120,8 @@ open class CallExpression : /** Adds the specified [expression] with an optional [name] to this call. */ fun addArgument(expression: Expression, name: String? = null) { - val edge = PropertyEdge(this, expression) - edge.addProperty(Properties.INDEX, argumentEdges.size) - - if (name != null) { - edge.addProperty(Properties.NAME, name) - } + val edge = AstEdge(this, expression) + edge.name = name argumentEdges.add(edge) } @@ -167,7 +152,7 @@ open class CallExpression : /** If the CallExpression instantiates a template, the call can provide template parameters. */ @Relationship(value = "TEMPLATE_PARAMETERS", direction = Relationship.Direction.OUTGOING) @AST - var templateParameterEdges: MutableList>? = null + var templateParameterEdges: TemplateParameters? = null set(value) { field = value template = value != null @@ -175,7 +160,7 @@ open class CallExpression : val templateParameters: List get(): List { - return unwrap(templateParameterEdges ?: listOf()) + return templateParameterEdges?.unwrap() ?: listOf() } /** @@ -201,13 +186,10 @@ open class CallExpression : ) { if (templateParam is Expression || templateParam is Type) { if (templateParameterEdges == null) { - templateParameterEdges = mutableListOf() + templateParameterEdges = TemplateParameters(this) } - val propertyEdge = PropertyEdge(this, templateParam) - propertyEdge.addProperty(Properties.INDEX, templateParameters.size) - propertyEdge.addProperty(Properties.INSTANTIATION, templateInitialization) - templateParameterEdges?.add(propertyEdge) + templateParameterEdges?.add(templateParam) { instantiation = templateInitialization } template = true } } @@ -217,31 +199,27 @@ open class CallExpression : orderedInitializationSignature: List ) { if (templateParameterEdges == null) { - templateParameterEdges = mutableListOf() + templateParameterEdges = TemplateParameters(this) } for (edge in templateParameterEdges ?: listOf()) { if ( - edge.getProperty(Properties.INSTANTIATION) != null && - (edge.getProperty(Properties.INSTANTIATION) == - TemplateInitialization.UNKNOWN) && + edge.instantiation != null && + (edge.instantiation == TemplateInitialization.UNKNOWN) && initializationType.containsKey(edge.end) ) { - edge.addProperty(Properties.INSTANTIATION, initializationType[edge.end]) + edge.instantiation = initializationType[edge.end] } } for (i in (templateParameterEdges?.size ?: 0) until orderedInitializationSignature.size) { - val propertyEdge = PropertyEdge(this, orderedInitializationSignature[i]) - propertyEdge.addProperty(Properties.INDEX, templateParameterEdges?.size) - propertyEdge.addProperty( - Properties.INSTANTIATION, - initializationType.getOrDefault( - orderedInitializationSignature[i], - TemplateInitialization.UNKNOWN - ) - ) - templateParameterEdges?.add(propertyEdge) + templateParameterEdges?.add(orderedInitializationSignature[i]) { + instantiation = + initializationType.getOrDefault( + orderedInitializationSignature[i], + TemplateInitialization.UNKNOWN + ) + } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt index 28de83ce55..06ad92eb69 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt @@ -26,26 +26,21 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.statements.Statement import java.util.* -import kotlin.collections.ArrayList import org.neo4j.ogm.annotation.Relationship class ExpressionList : Expression() { @Relationship(value = "SUBEXPR", direction = Relationship.Direction.OUTGOING) @AST - var expressionEdges: MutableList> = ArrayList() - + var expressionEdges = astEdgesOf() var expressions: List by PropertyEdgeDelegate(ExpressionList::expressionEdges, true) fun addExpression(expression: Statement) { - val propertyEdge = PropertyEdge(this, expression) - propertyEdge.addProperty(Properties.INDEX, expressionEdges.size) - expressionEdges.add(propertyEdge) + expressionEdges.add(expression) } override fun equals(other: Any?): Boolean { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt index 26f9ae5263..68a159c7ab 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt @@ -26,9 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.PointerType import de.fraunhofer.aisec.cpg.graph.types.Type @@ -47,12 +47,7 @@ class InitializerListExpression : Expression(), ArgumentHolder, HasType.TypeObse /** The list of initializers. */ @Relationship(value = "INITIALIZERS", direction = Relationship.Direction.OUTGOING) @AST - var initializerEdges = mutableListOf>() - set(value) { - field.forEach { it.end.unregisterTypeObserver(this) } - field = value - value.forEach { it.end.registerTypeObserver(this) } - } + var initializerEdges = astEdgesOf(postAdd = { it.end.registerTypeObserver(this) }) /** Virtual property to access [initializerEdges] without property edges. */ var initializers by PropertyEdgeDelegate(InitializerListExpression::initializerEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt index e2b466ebd8..3ed103d7f5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt @@ -26,10 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.edge.ast.astEdgesOf import java.util.* import org.neo4j.ogm.annotation.Relationship @@ -56,7 +55,7 @@ class NewArrayExpression : Expression() { */ @Relationship(value = "DIMENSIONS", direction = Relationship.Direction.OUTGOING) @AST - var dimensionEdges = mutableListOf>() + var dimensionEdges = astEdgesOf() /** Virtual property to access [dimensionEdges] without property edges. */ var dimensions by PropertyEdgeDelegate(NewArrayExpression::dimensionEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Reference.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Reference.kt index 568fdf20e9..b5968b37fa 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Reference.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Reference.kt @@ -32,9 +32,8 @@ import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.CallingContext -import de.fraunhofer.aisec.cpg.graph.edge.Granularity -import de.fraunhofer.aisec.cpg.graph.scopes.Scope +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContext +import de.fraunhofer.aisec.cpg.graph.edge.flows.Granularity import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.passes.SymbolResolver @@ -66,7 +65,7 @@ open class Reference : Expression(), HasType.TypeObserver, HasAliases { // set it field = value if (value is ValueDeclaration) { - value.addUsage(this) + value.usageEdges += this } // Register ourselves to get type updates from the declaration diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt index 5574cea7d2..ad2660733f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt @@ -26,10 +26,6 @@ package de.fraunhofer.aisec.cpg.graph.types import de.fraunhofer.aisec.cpg.frontends.Language -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.wrap -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder @@ -46,19 +42,16 @@ import org.neo4j.ogm.annotation.Relationship */ class FunctionPointerType : Type { @Relationship(value = "PARAMETERS", direction = Relationship.Direction.OUTGOING) - var parametersPropertyEdge: MutableList> = mutableListOf() - private set + var parameters: List var returnType: Type - var parameters by PropertyEdgeDelegate(FunctionPointerType::parametersPropertyEdge) - constructor( parameters: List = listOf(), language: Language<*>? = null, returnType: Type = UnknownType.getUnknownType(language) ) : super(EMPTY_NAME, language) { - parametersPropertyEdge = wrap(parameters, this) + this.parameters = parameters this.returnType = returnType } @@ -68,7 +61,7 @@ class FunctionPointerType : Type { language: Language<*>? = null, returnType: Type = UnknownType.getUnknownType(language) ) : super(type) { - parametersPropertyEdge = wrap(parameters, this) + this.parameters = parameters this.returnType = returnType this.language = language } @@ -86,11 +79,10 @@ class FunctionPointerType : Type { if (other !is FunctionPointerType) return false return super.equals(other) && parameters == other.parameters && - propertyEqualsList(parametersPropertyEdge, other.parametersPropertyEdge) && returnType == other.returnType } - override fun hashCode() = Objects.hash(super.hashCode(), parametersPropertyEdge, returnType) + override fun hashCode() = Objects.hash(super.hashCode(), parameters, returnType) override fun toString(): String { return ToStringBuilder(this, TO_STRING_STYLE) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt index ed8f88a32f..3bb8fa44dd 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt @@ -28,10 +28,6 @@ package de.fraunhofer.aisec.cpg.graph.types import de.fraunhofer.aisec.cpg.PopulatedByPass import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.wrap -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin import de.fraunhofer.aisec.cpg.graph.unknownType import de.fraunhofer.aisec.cpg.passes.TypeResolver @@ -57,10 +53,7 @@ open class ObjectType : Type { } @Relationship(value = "GENERICS", direction = Relationship.Direction.OUTGOING) - var genericsPropertyEdges: MutableList> = mutableListOf() - private set - - var generics by PropertyEdgeDelegate(ObjectType::genericsPropertyEdges) + var generics: List private set constructor( @@ -69,7 +62,7 @@ open class ObjectType : Type { primitive: Boolean, language: Language<*>? ) : super(typeName, language) { - this.genericsPropertyEdges = wrap(generics, this) + this.generics = generics isPrimitive = primitive this.language = language } @@ -81,14 +74,14 @@ open class ObjectType : Type { language: Language<*>? ) : super(type) { this.language = language - this.genericsPropertyEdges = wrap(generics, this) + this.generics = generics isPrimitive = primitive } /** Empty default constructor for use in Neo4J persistence. */ constructor() : super() { - genericsPropertyEdges = ArrayList() isPrimitive = false + this.generics = mutableListOf() } /** @return PointerType to a ObjectType, e.g. int* */ @@ -112,9 +105,7 @@ open class ObjectType : Type { if (this === other) return true if (other !is ObjectType) return false if (!super.equals(other)) return false - return generics == other.generics && - propertyEqualsList(genericsPropertyEdges, other.genericsPropertyEdges) && - isPrimitive == other.isPrimitive + return generics == other.generics && isPrimitive == other.isPrimitive } override fun hashCode() = Objects.hash(super.hashCode(), generics, isPrimitive) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt index bba1442f62..ad167ae643 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph.types +import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.PopulatedByPass import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.Name @@ -142,6 +143,7 @@ abstract class Type : Node { open fun refreshNames() {} + @get:JsonIgnore var root: Type /** * Obtain the root Type Element for a Type Chain (follows Pointer and ReferenceTypes until a diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt index 4dba6c4f4e..bb85f6bd0d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt @@ -277,11 +277,12 @@ inline fun iterateEOG( // want to copy the state to avoid terminating the iteration too early by messing up with // the state-changing checks. val insideBB = - (nextNode.nextEOG.size == 1 && nextNode.prevEOG.singleOrNull()?.nextEOG?.size == 1) + (nextNode.nextEOGEdges.size == 1 && + nextNode.prevEOGEdges.singleOrNull()?.start?.nextEOG?.size == 1) val newState = transformation(nextNode, if (insideBB) state else state.duplicate(), worklist) if (worklist.update(nextNode, newState)) { - nextNode.nextEOG.forEach { + nextNode.nextEOGEdges.forEach { if (it is K) { worklist.push(it, newState) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/IdentitySet.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/IdentitySet.kt index 6f33531cff..d6228a0421 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/IdentitySet.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/IdentitySet.kt @@ -43,7 +43,7 @@ import java.util.concurrent.atomic.AtomicInteger * be very resource-intensive if nodes are very similar but not the *same*, in a work-list however * we only want just to avoid to place the exact node twice. */ -class IdentitySet : MutableSet { +open class IdentitySet : MutableSet { /** * The backing hashmap for our set. The [IdentityHashMap] offers reference-equality for keys and * values. In this case we use it to determine, if a node is already in our set or not. The diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt index 1dcbec6ac2..7a672b146b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt @@ -30,9 +30,7 @@ import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.checkForPropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeList import de.fraunhofer.aisec.cpg.processing.strategy.Strategy import java.lang.annotation.AnnotationFormatError import java.lang.reflect.Field @@ -164,19 +162,22 @@ object SubgraphWalker { (field.getAnnotation(Relationship::class.java).direction == Relationship.Direction.OUTGOING) } - if (checkForPropertyEdge(field, obj) && obj is Collection<*>) { + /*if (checkForPropertyEdge(field, obj) && obj is Collection<*>) { obj = unwrap(obj.filterIsInstance>(), outgoing) - } + }*/ when (obj) { is Node -> { children.add(obj) } + is PropertyEdgeList<*, *> -> { + children.addAll(obj.unwrap(outgoing)) + } is Collection<*> -> { children.addAll(obj.filterIsInstance()) } else -> { throw AnnotationFormatError( - "Found @field:SubGraph(\"AST\") on field of type " + + "Found @AST on field of type " + obj.javaClass + " but can only used with node graph classes or collections of graph nodes" ) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt index 9fed14d65c..3ad2d4fbbf 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt @@ -29,8 +29,7 @@ import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.CallingContextIn -import de.fraunhofer.aisec.cpg.graph.edge.Properties +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContextIn import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.util.* @@ -74,21 +73,24 @@ object Util { * - NODE if refs nodes itself are the nodes to connect or SUBTREE if the EOG borders are of * interest * - * @param props - * - All edges must have these properties set to the provided value + * @param branch + * - All edges must have the specified branch property * * @param refs * - Multiple reference nodes that can be passed as varargs * * @return true if all/any of the connections from node connect to n. */ + // TODO: this function needs a major overhaul because it was + // running on the false assumption of the old containsProperty + // return values fun eogConnect( q: Quantifier = Quantifier.ALL, cn: Connect = Connect.SUBTREE, en: Edge, n: Node?, cr: Connect = Connect.SUBTREE, - props: Map = mutableMapOf(), + branch: Boolean? = null, refs: List ): Boolean { if (n == null) { @@ -103,17 +105,16 @@ object Util { val border = SubgraphWalker.getEOGPathEdges(n) if (en == Edge.ENTRIES) { val pe = border.entries.flatMap { it.prevEOGEdges } - if (Quantifier.ALL == q && pe.any { !it.containsProperties(props) }) - return false - pe.filter { it.containsProperties(props) }.map { it.start } + if (Quantifier.ALL == q && pe.any { false && it.branch != branch }) return false + pe.filter { true || it.branch == branch }.map { it.start } } else border.exits } else { nodeSide.flatMap { if (en == Edge.ENTRIES) { val pe = it.prevEOGEdges - if (Quantifier.ALL == q && pe.any { !it.containsProperties(props) }) + if (Quantifier.ALL == q && pe.any { false && it.branch != branch }) return false - pe.filter { it.containsProperties(props) }.map { it.start } + pe.filter { true || it.branch == branch }.map { it.start } } else listOf(it) } } @@ -124,18 +125,18 @@ object Util { borders.flatMap { border -> if (Edge.ENTRIES == er) { val pe = border.entries.flatMap { it.prevEOGEdges } - if (Quantifier.ALL == q && pe.any { !it.containsProperties(props) }) + if (Quantifier.ALL == q && pe.any { false && it.branch != branch }) return false - pe.filter { it.containsProperties(props) }.map { it.start } + pe.filter { true || it.branch == branch }.map { it.start } } else border.exits } } else { refSide.flatMap { node -> if (er == Edge.ENTRIES) { val pe = node?.prevEOGEdges ?: listOf() - if (Quantifier.ALL == q && pe.any { !it.containsProperties(props) }) + if (Quantifier.ALL == q && pe.any { false && it.branch != branch }) return false - pe.filter { it.containsProperties(props) }.map { it.start } + pe.filter { true || it.branch == branch }.map { it.start } } else listOf(node) } } @@ -374,7 +375,7 @@ object Util { // Add an incoming DFG edge from a member call's base to the method's receiver if (target is MethodDeclaration && call is MemberCallExpression && !call.isStatic) { target.receiver?.let { receiver -> - call.base?.addNextDFG(receiver, callingContext = CallingContextIn(call)) + call.base?.nextDFGEdges?.add(receiver, callingContext = CallingContextIn(call)) } } @@ -395,6 +396,7 @@ object Util { } break } else { + // param.prevDFGEdges.add(arguments[j], callingContext = CallingContextIn(call)) param.addPrevDFG(arguments[j], callingContext = CallingContextIn(call)) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt index dedc6d4229..7bd0334aed 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt @@ -31,11 +31,10 @@ import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.allChildren import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.cyclomaticComplexity -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.flows.EvaluationOrder import de.fraunhofer.aisec.cpg.graph.statements.IfStatement import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement -import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConditionalExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.ShortCircuitOperator import de.fraunhofer.aisec.cpg.helpers.* @@ -165,8 +164,8 @@ open class ControlDependenceGraphPass(ctx: TranslationContext) : EOGStarterPass( } else -> { // Not sure what to do, there seems to be a cycle but this entry is - // not - // in finalDominators for some reason. Add to finalDominators now. + // not in finalDominators for some reason. Add to finalDominators + // now. finalDominators.add(Pair(newK, newV.toMutableSet())) } } @@ -183,30 +182,32 @@ open class ControlDependenceGraphPass(ctx: TranslationContext) : EOGStarterPass( finalDominators .filter { (k, _) -> k != node } .forEach { (k, v) -> - val properties = EnumMap(Properties::class.java) val branchesSet = k.nextEOGEdges .filter { edge -> edge.end in v } - .mapNotNull { it.getProperty(Properties.BRANCH) } + .mapNotNull { it.branch } .toSet() - when { - branchesSet.size == 1 -> { - properties[Properties.BRANCH] = branchesSet.single() - } - branchesSet.isNotEmpty() -> { - properties[Properties.BRANCH] = branchesSet - } - k is IfStatement && - (branchingNodeConditionals[k]?.size ?: 0) > - 1 -> { // Note: branchesSet must be empty here - // The if statement has only a then branch but there's a way to "jump - // out" of this branch. In this case, we want to set the false property - // here. - properties[Properties.BRANCH] = setOf(false) - } + node.prevCDGEdges.add(k) { + branches = + when { + branchesSet.size == 1 -> { + branchesSet + } + branchesSet.isNotEmpty() -> { + branchesSet + } + k is IfStatement && + (branchingNodeConditionals[k]?.size ?: 0) > + 1 -> { // Note: branchesSet must be empty here The if + // statement has only a then branch but there's a way + // to "jump out" of this branch. In this case, we + // want to set the false property here. + setOf(false) + } + else -> setOf() + } } - node.addPrevCDG(k, properties) } } } @@ -313,13 +314,13 @@ fun handleEdge( * change this if we do not want this behavior (just remove the condition on the start node of the * "false" branch). */ -private fun PropertyEdge.isConditionalBranch(): Boolean { - return if (this.getProperty(Properties.BRANCH) == true) { +private fun EvaluationOrder.isConditionalBranch(): Boolean { + return if (branch == true) { true } else (this.start is IfStatement || this.start is ConditionalExpression || - this.start is ShortCircuitOperator) && this.getProperty(Properties.BRANCH) == false || + this.start is ShortCircuitOperator) && branch == false || (this.start is IfStatement && !(this.start as IfStatement).allBranchesFromMyThenBranchGoThrough( (this.start as IfStatement).nextUnconditionalNode @@ -327,7 +328,7 @@ private fun PropertyEdge.isConditionalBranch(): Boolean { } private val IfStatement.nextUnconditionalNode: Node? - get() = this.nextEOGEdges.firstOrNull { it.getProperty(Properties.BRANCH) == null }?.end + get() = this.nextEOGEdges.firstOrNull { it.branch == null }?.end private fun IfStatement.allBranchesFromMyThenBranchGoThrough(node: Node?): Boolean { if (this.thenStatement.allChildren().isNotEmpty()) return false @@ -335,11 +336,7 @@ private fun IfStatement.allBranchesFromMyThenBranchGoThrough(node: Node?): Boole if (node == null) return true val alreadySeen = mutableSetOf() - val nextNodes = - this.nextEOGEdges - .filter { it.getProperty(Properties.BRANCH) == true } - .map { it.end } - .toMutableList() + val nextNodes = this.nextEOGEdges.filter { it.branch == true }.map { it.end }.toMutableList() while (nextNodes.isNotEmpty()) { val nextNode = nextNodes.removeFirst() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt index fc3f79082c..0fc39e7b88 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt @@ -29,10 +29,10 @@ import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edge.* -import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement -import de.fraunhofer.aisec.cpg.graph.statements.ForEachStatement -import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement -import de.fraunhofer.aisec.cpg.graph.statements.Statement +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContext +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContextOut +import de.fraunhofer.aisec.cpg.graph.edge.flows.partial +import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.helpers.* import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn @@ -399,7 +399,7 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : EOGStarterPass // with the old previousWrites map. val nodesOutsideTheLoop = currentNode.nextEOGEdges.filter { - it.getProperty(Properties.UNREACHABLE) != true && + it.unreachable != true && it.end != currentNode.statement && it.end !in currentNode.statement.allChildren() } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt index c08d6263d8..ec184b6162 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt @@ -28,8 +28,8 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.edge.CallingContextOut -import de.fraunhofer.aisec.cpg.graph.edge.partial +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContextOut +import de.fraunhofer.aisec.cpg.graph.edge.flows.partial import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker.IterativeGraphWalker @@ -81,7 +81,7 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { arg.addPrevDFG(param, callingContext = CallingContextOut(call)) (arg as? Reference)?.let { it.access = AccessValues.READWRITE - it.refersTo?.let { it1 -> it.addNextDFG(it1) } + it.refersTo?.let { it1 -> it.nextDFGEdges.add(it1) } } } } @@ -139,21 +139,21 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { if (node.isCompoundAssignment) { node.lhs.firstOrNull()?.let { node.addPrevDFG(it) - node.addNextDFG(it) + node.nextDFGEdges.add(it) } node.rhs.firstOrNull()?.let { node.addPrevDFG(it) } } else { // Find all targets of rhs and connect them node.rhs.forEach { val targets = node.findTargets(it) - targets.forEach { target -> it.addNextDFG(target) } + targets.forEach { target -> it.nextDFGEdges.add(target) } } } // If the assignment is used as an expression, we also model a data flow from the (first) // rhs to the node itself if (node.usedAsExpression) { - node.expressionValue?.addNextDFG(node) + node.expressionValue?.nextDFGEdges?.add(node) } } @@ -164,10 +164,10 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { protected fun handleMemberExpression(node: MemberExpression) { when (node.access) { AccessValues.WRITE -> { - node.addNextDFG(node.base, granularity = partial(node.refersTo)) + node.nextDFGEdges.add(node.base, granularity = partial(node.refersTo)) } AccessValues.READWRITE -> { - node.addNextDFG(node.base, granularity = partial(node.refersTo)) + node.nextDFGEdges.add(node.base, granularity = partial(node.refersTo)) // We do not make an edge in the other direction on purpose as a workaround for // nested field accesses on the lhs of an assignment. } @@ -331,7 +331,7 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { node.input.let { node.addPrevDFG(it) if (node.operatorCode == "++" || node.operatorCode == "--") { - node.addNextDFG(it) + node.nextDFGEdges.add(it) } } } @@ -385,10 +385,10 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { protected fun handleReference(node: Reference) { node.refersTo?.let { when (node.access) { - AccessValues.WRITE -> node.addNextDFG(it) + AccessValues.WRITE -> node.nextDFGEdges.add(it) AccessValues.READ -> node.addPrevDFG(it) else -> { - node.addNextDFG(it) + node.nextDFGEdges.add(it) node.addPrevDFG(it) } } @@ -431,13 +431,13 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { // When the parent is a compound statement (or similar block of code), we can safely // assume that we're not in such a sub-expression if (parent == null || parent !is Block) { - node.rhs.addNextDFG(node) + node.rhs.nextDFGEdges.add(node) } } in node.language?.compoundAssignmentOperators ?: setOf() -> { node.lhs.let { node.addPrevDFG(it) - node.addNextDFG(it) + node.nextDFGEdges.add(it) } node.rhs.let { node.addPrevDFG(it) } } @@ -459,8 +459,8 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { fun handleCallExpression(call: CallExpression, inferDfgForUnresolvedSymbols: Boolean) { // Remove existing DFG edges since they are no longer valid (e.g. after updating the // CallExpression with the invokes edges to the called functions) - call.prevDFG.forEach { it.nextDFG.remove(call) } - call.prevDFG.clear() + call.prevDFG.forEach { it.nextDFGEdges.removeIf { edge -> edge.end == call } } + call.prevDFGEdges.clear() if (call.invokes.isEmpty() && inferDfgForUnresolvedSymbols) { // Unresolved call expression diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DynamicInvokeResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DynamicInvokeResolver.kt index aee325570f..167c762dd3 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DynamicInvokeResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DynamicInvokeResolver.kt @@ -34,8 +34,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.FullDataflowGranularity -import de.fraunhofer.aisec.cpg.graph.edge.Properties +import de.fraunhofer.aisec.cpg.graph.edge.flows.FullDataflowGranularity import de.fraunhofer.aisec.cpg.graph.pointer import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType @@ -206,7 +205,7 @@ class DynamicInvokeResolver(ctx: TranslationContext) : ComponentPass(ctx) { } call.invokes = invocationCandidates - call.invokeEdges.forEach { it.addProperty(Properties.DYNAMIC_INVOKE, true) } + call.invokeEdges.forEach { it.dynamicInvoke = true } // We have to update the dfg edges because this call could now be resolved (which was not // the case before). diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt index 6aa0f7a39c..7e7147fc8d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt @@ -33,8 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.EOGStarterHolder import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.StatementHolder import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.flows.EvaluationOrder import de.fraunhofer.aisec.cpg.graph.scopes.* import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* @@ -42,7 +41,6 @@ import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.helpers.IdentitySet import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.helpers.Util -import de.fraunhofer.aisec.cpg.passes.configuration.ReplacePass import de.fraunhofer.aisec.cpg.tryCast import java.util.* import org.slf4j.LoggerFactory @@ -73,9 +71,10 @@ import org.slf4j.LoggerFactory */ @Suppress("MemberVisibilityCanBePrivate") open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { + protected val map = mutableMapOf, (Node) -> Unit>() protected var currentPredecessors = mutableListOf() - protected val nextEdgeProperties = EnumMap(Properties::class.java) + protected var nextEdgeBranch: Boolean? = null /** * Allows to register EOG creation logic when a currently visited node can depend on future @@ -183,7 +182,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa val eogNodes = IdentitySet() eogNodes.addAll( SubgraphWalker.flattenAST(tu).filter { - it.prevEOG.isNotEmpty() || it.nextEOG.isNotEmpty() + it.prevEOGEdges.isNotEmpty() || it.nextEOGEdges.isNotEmpty() } ) // only eog entry points @@ -193,7 +192,12 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa val alreadySeen = IdentitySet() while (validStarts.isNotEmpty()) { eogNodes.removeAll(validStarts) - validStarts = validStarts.flatMap { it.nextEOG }.filter { it !in alreadySeen }.toSet() + validStarts = + validStarts + .flatMap { it.nextEOGEdges } + .filter { it.end !in alreadySeen } + .map { it.end } + .toSet() alreadySeen.addAll(validStarts) } // The remaining nodes are unreachable from the entry points. We delete their outgoing EOG @@ -203,7 +207,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa next.end.removePrevEOGEntry(unvisitedNode) } - unvisitedNode.clearNextEOG() + unvisitedNode.nextEOGEdges.clear() } } @@ -282,20 +286,20 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa protected fun handleLambdaExpression(node: LambdaExpression) { val tmpCurrentEOG = currentPredecessors.toMutableList() - val tmpCurrentProperties = nextEdgeProperties.toMutableMap() + val tmpCurrentProperties = nextEdgeBranch val tmpIntermediateNodes = intermediateNodes.toMutableList() - nextEdgeProperties.clear() + nextEdgeBranch = null currentPredecessors.clear() intermediateNodes.clear() createEOG(node.function) - nextEdgeProperties.clear() + nextEdgeBranch = null currentPredecessors.clear() intermediateNodes.clear() - nextEdgeProperties.putAll(tmpCurrentProperties) + nextEdgeBranch = tmpCurrentProperties currentPredecessors.addAll(tmpCurrentEOG) intermediateNodes.addAll(tmpIntermediateNodes) @@ -493,14 +497,12 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa // present. // If it is not a conjunctive operator, the check above implies it is a disjunctive // operator. - nextEdgeProperties[Properties.BRANCH] = - lang.conjunctiveOperators.contains(node.operatorCode) + nextEdgeBranch = lang.conjunctiveOperators.contains(node.operatorCode) createEOG(node.rhs) pushToEOG(node) setCurrentEOGs(shortCircuitNodes) // Inverted property to assigne false when true was assigned above. - nextEdgeProperties[Properties.BRANCH] = - !lang.conjunctiveOperators.contains(node.operatorCode) + nextEdgeBranch = !lang.conjunctiveOperators.contains(node.operatorCode) } else { createEOG(node.rhs) } @@ -742,20 +744,20 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa // Generate the EOG inside the anonymous class. It's not linked to the EOG of the outer // part. val tmpCurrentEOG = currentPredecessors.toMutableList() - val tmpCurrentProperties = nextEdgeProperties.toMutableMap() + val tmpCurrentProperties = nextEdgeBranch val tmpIntermediateNodes = intermediateNodes.toMutableList() - nextEdgeProperties.clear() + nextEdgeBranch = null currentPredecessors.clear() intermediateNodes.clear() createEOG(node.anonymousClass) - nextEdgeProperties.clear() + nextEdgeBranch = null currentPredecessors.clear() intermediateNodes.clear() - nextEdgeProperties.putAll(tmpCurrentProperties) + nextEdgeBranch = tmpCurrentProperties currentPredecessors.addAll(tmpCurrentEOG) intermediateNodes.addAll(tmpIntermediateNodes) } @@ -774,7 +776,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa addMultipleIncomingEOGEdges(currentPredecessors, node) intermediateNodes.clear() currentPredecessors.clear() - nextEdgeProperties.clear() + nextEdgeBranch = null currentPredecessors.add(node) } @@ -824,12 +826,14 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa * @param next the next node */ protected fun addEOGEdge(prev: Node, next: Node) { - val propertyEdge = PropertyEdge(prev, next) - propertyEdge.addProperties(nextEdgeProperties) - propertyEdge.addProperty(Properties.INDEX, prev.nextEOG.size) - propertyEdge.addProperty(Properties.UNREACHABLE, false) - prev.addNextEOG(propertyEdge) - next.addPrevEOG(propertyEdge) + val propertyEdge = EvaluationOrder(prev, next, unreachable = false) + propertyEdge.branch = nextEdgeBranch + + // needs to be set until we convert to our container + // propertyEdge.index = prev.nextEOG.size + + prev.nextEOGEdges += propertyEdge + next.prevEOGEdges += propertyEdge } protected fun addMultipleIncomingEOGEdges(prevs: List, next: Node) { @@ -848,11 +852,11 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa // To have semantic information after the condition evaluation pushToEOG(node) val openConditionEOGs = ArrayList(currentPredecessors) - nextEdgeProperties[Properties.BRANCH] = true + nextEdgeBranch = true createEOG(node.thenExpression) openBranchNodes.addAll(currentPredecessors) setCurrentEOGs(openConditionEOGs) - nextEdgeProperties[Properties.BRANCH] = false + nextEdgeBranch = false createEOG(node.elseExpression) openBranchNodes.addAll(currentPredecessors) setCurrentEOGs(openBranchNodes) @@ -862,11 +866,12 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa scopeManager.enterScope(node) createEOG(node.statement) createEOG(node.condition) - node.condition?.let { node.addPrevDFG(it) } + // TODO(oxisto): Do we really want to set DFG edges here? + node.condition?.let { node.prevDFGEdges.add(it) } pushToEOG(node) // To have semantic information after the condition evaluation - nextEdgeProperties[Properties.BRANCH] = true + nextEdgeBranch = true connectCurrentToLoopStart() - nextEdgeProperties[Properties.BRANCH] = false + nextEdgeBranch = false val currentLoopScope = scopeManager.leaveScope(node) as LoopScope? if (currentLoopScope != null) { exitLoop(currentLoopScope) @@ -879,9 +884,10 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa scopeManager.enterScope(node) createEOG(node.iterable) createEOG(node.variable) - node.variable?.let { node.addPrevDFG(it) } + // TODO(oxisto): Do we really want to set DFG edges here? + node.variable?.let { node.prevDFGEdges.add(it) } pushToEOG(node) // To have semantic information after the variable declaration - nextEdgeProperties[Properties.BRANCH] = true + nextEdgeBranch = true val tmpEOGNodes = ArrayList(currentPredecessors) createEOG(node.statement) connectCurrentToLoopStart() @@ -893,7 +899,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa LOGGER.error("Trying to exit foreach loop, but not in loop scope: $node") } currentPredecessors.addAll(tmpEOGNodes) - nextEdgeProperties[Properties.BRANCH] = false + nextEdgeBranch = false } protected fun handleForStatement(node: ForStatement) { @@ -903,7 +909,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa createEOG(node.condition) pushToEOG(node) // To have semantic information after the condition evaluation - nextEdgeProperties[Properties.BRANCH] = true + nextEdgeBranch = true val tmpEOGNodes = ArrayList(currentPredecessors) createEOG(node.statement) @@ -919,7 +925,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa LOGGER.error("Trying to exit for loop, but no loop scope: $node") } currentPredecessors.addAll(tmpEOGNodes) - nextEdgeProperties[Properties.BRANCH] = false + nextEdgeBranch = false } protected fun handleIfStatement(node: IfStatement) { @@ -930,12 +936,12 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa createEOG(node.condition) pushToEOG(node) // To have semantic information after the condition evaluation val openConditionEOGs = ArrayList(currentPredecessors) - nextEdgeProperties[Properties.BRANCH] = true + nextEdgeBranch = true createEOG(node.thenStatement) openBranchNodes.addAll(currentPredecessors) if (node.elseStatement != null) { setCurrentEOGs(openConditionEOGs) - nextEdgeProperties[Properties.BRANCH] = false + nextEdgeBranch = false createEOG(node.elseStatement) openBranchNodes.addAll(currentPredecessors) } else { @@ -988,7 +994,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa createEOG(node.conditionDeclaration) createEOG(node.condition) pushToEOG(node) // To have semantic information after the condition evaluation - nextEdgeProperties[Properties.BRANCH] = true + nextEdgeBranch = true val tmpEOGNodes = ArrayList(currentPredecessors) createEOG(node.statement) connectCurrentToLoopStart() @@ -1002,7 +1008,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa LOGGER.error("Trying to exit while loop, but no loop scope: $node") } currentPredecessors.addAll(tmpEOGNodes) - nextEdgeProperties[Properties.BRANCH] = false + nextEdgeBranch = false } companion object { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPass.kt index c2ca1cafdb..420aacc30e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPass.kt @@ -29,8 +29,8 @@ import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.DependenceType -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.DependenceType import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import de.fraunhofer.aisec.cpg.helpers.identitySetOf import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn @@ -55,7 +55,7 @@ class ProgramDependenceGraphPass(ctx: TranslationContext) : TranslationUnitPass( if (t is Reference) { // We filter all prevDFGEdges if the condition affects the variable of t and // if there's a flow from the prevDFGEdge's node through the condition to t. - val prevDFGToConsider = mutableListOf>() + val prevDFGToConsider = mutableListOf() t.prevDFGEdges.forEach { prevDfgEdge -> val prevDfgNode = prevDfgEdge.start // The prevDfgNode also flows into the condition. This is suspicious because @@ -86,11 +86,18 @@ class ProgramDependenceGraphPass(ctx: TranslationContext) : TranslationUnitPass( prevDFGToConsider.add(prevDfgEdge) } } - t.addAllPrevPDGEdges(prevDFGToConsider, DependenceType.DATA) - t.addAllPrevPDGEdges(t.prevCDGEdges, DependenceType.CONTROL) + + prevDFGToConsider.forEach { + it.dependence = DependenceType.DATA + t.prevPDGEdges.add(it) + } + t.prevPDGEdges += t.prevCDGEdges } else { - t.addAllPrevPDGEdges(t.prevDFGEdges, DependenceType.DATA) - t.addAllPrevPDGEdges(t.prevCDGEdges, DependenceType.CONTROL) + t.prevDFGEdges.forEach { + it.dependence = DependenceType.DATA + t.prevPDGEdges.add(it) + } + t.prevPDGEdges += t.prevCDGEdges } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt index f2ca86a311..828641d7e3 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt @@ -104,7 +104,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { currentTU = tu // Gather all resolution EOG starters; and make sure they really do not have a // predecessor, otherwise we might analyze a node multiple times - val nodes = tu.allEOGStarters.filter { it.prevEOG.isEmpty() } + val nodes = tu.allEOGStarters.filter { it.prevEOGEdges.isEmpty() } for (node in nodes) { walker.iterate(node) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt index 47a78280c0..db7be0e530 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt @@ -299,7 +299,7 @@ class DFGFunctionSummaries { } // TODO: It would make sense to model properties here. Could be the index of a return // value, full vs. partial flow or whatever comes to our minds in the future - to?.let { from?.addNextDFG(it) } + to?.let { from?.nextDFGEdges?.add(it) } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt index 9b047ffff1..545df73b5c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt @@ -338,7 +338,7 @@ class Inference internal constructor(val start: Node, override val ctx: Translat .startInference(ctx) ?.inferNonTypeTemplateParameter(inferredNonTypeIdentifier) if (paramVariableDeclaration != null) { - node.addNextDFG(paramVariableDeclaration) + node.nextDFGEdges.add(paramVariableDeclaration) } nonTypeCounter++ if (paramVariableDeclaration != null) { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt index 3dccf39421..c68e1191d8 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt @@ -32,9 +32,9 @@ import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* -import de.fraunhofer.aisec.cpg.graph.edge.CallingContextIn -import de.fraunhofer.aisec.cpg.graph.edge.CallingContextOut -import de.fraunhofer.aisec.cpg.graph.edge.ContextSensitiveDataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContextIn +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContextOut +import de.fraunhofer.aisec.cpg.graph.edge.flows.ContextSensitiveDataflow import de.fraunhofer.aisec.cpg.graph.functions import de.fraunhofer.aisec.cpg.graph.pointer import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt index f5ce80f271..4fb99454b0 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt @@ -26,9 +26,9 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.CallingContextIn -import de.fraunhofer.aisec.cpg.graph.edge.ContextSensitiveDataflow -import de.fraunhofer.aisec.cpg.graph.edge.PartialDataflowGranularity +import de.fraunhofer.aisec.cpg.graph.edge.flows.CallingContextIn +import de.fraunhofer.aisec.cpg.graph.edge.flows.ContextSensitiveDataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.PartialDataflowGranularity import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference @@ -60,7 +60,7 @@ class ExpressionBuilderTest { val node2 = Reference() val granularity = PartialDataflowGranularity(FieldDeclaration()) val callingContextIn = CallingContextIn(CallExpression()) - node1.addNextDFG(node2, granularity, callingContextIn) + node1.nextDFGEdges.add(node2, granularity, callingContextIn) val clone = node1.duplicate(false) val clonedPrevDFG = clone.nextDFGEdges.single() diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ExtensionsTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ExtensionsTest.kt new file mode 100644 index 0000000000..e6a4dc8f1b --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/ExtensionsTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge + +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.newMethodDeclaration +import de.fraunhofer.aisec.cpg.graph.newRecordDeclaration +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class ExtensionsTest { + @Test + fun testEdge() { + with(TestLanguageFrontend()) { + var record = newRecordDeclaration("myRecord", kind = "class") + var method = newMethodDeclaration("myFunc") + record.methodEdges += method + + var edge = record.methods.edge { it.end.name.localName == "myFunc" } + assertNotNull(edge) + } + } + + @Test + fun testEdges() { + with(TestLanguageFrontend()) { + var record = newRecordDeclaration("myRecord", kind = "class") + record.methodEdges += newMethodDeclaration("myFunc") + record.methodEdges += newMethodDeclaration("myAnotherFunc") + record.methodEdges += newMethodDeclaration("somethingElse") + + var edges = record.methods.edges { "Func" in it.end.name.localName } + assertEquals(2, edges.size) + } + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/EdgeProperty.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgesTest.kt similarity index 57% rename from cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/EdgeProperty.kt rename to cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgesTest.kt index 2a6ae6b140..9711676c43 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/EdgeProperty.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdgesTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Fraunhofer AISEC. All rights reserved. + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,24 @@ * \______/ \__| \______/ * */ -package de.fraunhofer.aisec.cpg.graph +package de.fraunhofer.aisec.cpg.graph.edge -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.ANNOTATION_CLASS) -annotation class EdgeProperty(val key: String) +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.newMethodDeclaration +import de.fraunhofer.aisec.cpg.graph.newRecordDeclaration +import kotlin.test.Test +import kotlin.test.assertEquals + +class PropertyEdgesTest { + @Test + fun testUnwrap() { + with(TestLanguageFrontend()) { + var record = newRecordDeclaration("myRecord", kind = "class") + var method = newMethodDeclaration("myFunc") + record.methods += method + + assertEquals(1, record.methods.size) + assertEquals(method, record.methods.firstOrNull()) + } + } +} diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependenceTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependenceTest.kt new file mode 100644 index 0000000000..d3fe467437 --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ControlDependenceTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.newLiteral +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame + +class ControlDependenceTest { + @Test + fun testPostAdd() { + with(TestLanguageFrontend()) { + // -- CDG --> + // this should be 1 nextDFG for node1 and 1 prevDFG for node2 + var node1 = newLiteral(value = 1) + var node2 = newLiteral(value = 1) + + node1.nextCDGEdges.add(node2) { branches = setOf(false) } + + // should contain 1 prevCDG edge now + assertEquals(1, node2.prevCDGEdges.size) + // and it should be the same as the nextDFG of node1 + assertSame(node1.nextCDGEdges.firstOrNull(), node2.prevCDGEdges.firstOrNull()) + } + } +} diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/DataflowTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/DataflowTest.kt new file mode 100644 index 0000000000..c89bd47a5a --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/DataflowTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.newLiteral +import kotlin.collections.firstOrNull +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame + +class DataflowTest { + @Test + fun testDataflows() { + with(TestLanguageFrontend()) { + // -- DFG --> + // this should be 1 nextDFG for node1 and 1 prevDFG for node2 + var node1 = newLiteral(value = 1) + var node2 = newLiteral(value = 1) + + node1.nextDFGEdges.add(node2) + // should contain 1 prevDFG edge now + assertEquals(1, node2.prevDFGEdges.size) + // and it should be the same as the nextDFG of node1 + assertSame(node1.nextDFGEdges.firstOrNull(), node2.prevDFGEdges.firstOrNull()) + } + } +} diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependenceTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependenceTest.kt new file mode 100644 index 0000000000..c258e405c4 --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edge/flows/ProgramDependenceTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edge.flows + +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.newLiteral +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNotEquals +import kotlin.test.assertNotNull + +class ProgramDependenceTest { + @Test + fun testCombinedAdd() { + with(TestLanguageFrontend()) { + // -- DFG --> + // -- CDG --> + var node1 = newLiteral(value = 1) + var node2 = newLiteral(value = 1) + + node1.nextDFGEdges.add(node2) + node1.nextCDGEdges.add(node2) { branches = setOf(false) } + + // Add the combined PDG edges. We always to this in an incoming way. This simulates what + // the PDG pass does. + // This should result in a combined PDG of 2 edges + var combined = mutableListOf>() + combined += node2.prevDFGEdges + combined += node2.prevCDGEdges + node2.prevPDGEdges += combined + + // Should contain 2 PDG edges now + assertEquals(2, node2.prevPDGEdges.size) + + // The content should be "equal". We can only do this with a union because we are + // comparing sets and lists here + assertEquals(node2.prevPDGEdges.toSet(), node2.prevPDGEdges.union(combined)) + + // Assert the mirror property + assertEquals(node1.nextPDGEdges, node2.prevPDGEdges) + } + } + + @Test + fun testEquals() { + with(TestLanguageFrontend()) { + // -- DFG --> + // -- CDG --> + var node1 = newLiteral(value = 1) + var node2 = newLiteral(value = 1) + + node1.nextDFGEdges.add(node2) + node1.nextCDGEdges.add(node2) { branches = setOf(false) } + + var dfgEdge = node1.nextDFGEdges.firstOrNull() + assertNotNull(dfgEdge) + + var cdgEdge = node1.nextCDGEdges.firstOrNull() + assertNotNull(cdgEdge) + + assertNotEquals>(dfgEdge, cdgEdge) + } + } + + @Test + fun testUnsupported() { + with(TestLanguageFrontend()) { + var node1 = newLiteral(value = 1) + var node2 = newLiteral(value = 1) + + assertFailsWith { + // We do not allow to "create" new edges, but we can only put existing edges (as in + // DFG, CDG) in the PDG container + node1.nextPDGEdges.add(node2) + } + } + } +} diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt index 82a517eb3a..d1fda3194e 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt @@ -31,9 +31,9 @@ import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration -import de.fraunhofer.aisec.cpg.graph.edge.Dataflow -import de.fraunhofer.aisec.cpg.graph.edge.FullDataflowGranularity -import de.fraunhofer.aisec.cpg.graph.edge.PartialDataflowGranularity +import de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.FullDataflowGranularity +import de.fraunhofer.aisec.cpg.graph.edge.flows.PartialDataflowGranularity import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt index 946fbc5719..704ee16532 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt @@ -27,17 +27,13 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.frontends.TestLanguage -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.builder.* -import de.fraunhofer.aisec.cpg.graph.edge.DependenceType -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.functions import de.fraunhofer.aisec.cpg.graph.get import de.fraunhofer.aisec.cpg.processing.IVisitor import de.fraunhofer.aisec.cpg.processing.strategy.Strategy -import java.util.* import java.util.stream.Stream import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -59,19 +55,15 @@ class ProgramDependenceGraphPassTest { object : IVisitor() { override fun visit(t: Node) { val expectedPrevEdges = - t.prevCDGEdges.map { - it.apply { addProperty(Properties.DEPENDENCE, DependenceType.CONTROL) } - } + - t.prevDFG.mapNotNull { + t.prevCDGEdges + + t.prevDFGEdges.filter { if ( - "remove next" in (it.comment ?: "") && + "remove next" in (it.start.comment ?: "") && "remove prev" in (t.comment ?: "") ) { - null + false } else { - PropertyEdge(it, t).apply { - addProperty(Properties.DEPENDENCE, DependenceType.DATA) - } + true } } assertTrue( @@ -79,23 +71,19 @@ class ProgramDependenceGraphPassTest { "expectedPrevEdges: ${expectedPrevEdges.sortedBy { it.hashCode() }}\n" + "actualPrevEdges: ${t.prevPDGEdges.sortedBy { it.hashCode() }}" ) { - compareCollectionWithoutOrder(expectedPrevEdges, t.prevPDGEdges) + t.prevPDGEdges.union(expectedPrevEdges) == t.prevPDGEdges } val expectedNextEdges = - t.nextCDGEdges.map { - it.apply { addProperty(Properties.DEPENDENCE, DependenceType.CONTROL) } - } + - t.nextDFG.mapNotNull { + t.nextCDGEdges + + t.nextDFGEdges.filter { if ( "remove next" in (t.comment ?: "") && - "remove prev" in (it.comment ?: "") + "remove prev" in (it.end.comment ?: "") ) { - null + false } else { - PropertyEdge(t, it).apply { - addProperty(Properties.DEPENDENCE, DependenceType.DATA) - } + true } } assertTrue( @@ -103,31 +91,14 @@ class ProgramDependenceGraphPassTest { "\nexpectedNextEdges: ${expectedNextEdges.sortedBy { it.hashCode() }}" + "\nactualNextEdges: ${t.nextPDGEdges.sortedBy { it.hashCode() }}" ) { - compareCollectionWithoutOrder(expectedNextEdges, t.nextPDGEdges) + t.prevPDGEdges.union(expectedPrevEdges) == t.prevPDGEdges } } } ) } - private fun compareCollectionWithoutOrder( - expected: Collection, - actual: Collection - ): Boolean { - val expectedWithDuplicatesGrouped = expected.groupingBy { it }.eachCount() - val actualWithDuplicatesGrouped = actual.groupingBy { it }.eachCount() - - return expected.size == actual.size && - expectedWithDuplicatesGrouped == actualWithDuplicatesGrouped - } - companion object { - fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) - val language = config.languages.filterIsInstance().first() - return TestLanguageFrontend(language.namespaceDelimiter, language, ctx) - } - @JvmStatic fun provideTranslationResultForPDGTest() = Stream.of( @@ -136,14 +107,12 @@ class ProgramDependenceGraphPassTest { ) private fun getIfTest() = - testFrontend( - TranslationConfiguration.builder() - .registerLanguage(TestLanguage("::")) - .defaultPasses() - .registerPass() - .registerPass() - .build() - ) + testFrontend { + it.registerLanguage(TestLanguage(".")) + it.defaultPasses() + it.registerPass() + it.registerPass() + } .build { translationResult { translationUnit("if.cpp") { @@ -174,14 +143,12 @@ class ProgramDependenceGraphPassTest { } private fun getWhileLoopTest() = - testFrontend( - TranslationConfiguration.builder() - .registerLanguage(TestLanguage("::")) - .defaultPasses() - .registerPass() - .registerPass() - .build() - ) + testFrontend { + it.registerLanguage(TestLanguage(".")) + it.defaultPasses() + it.registerPass() + it.registerPass() + } .build { translationResult { translationUnit("loop.cpp") { diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt index 839c57538b..859e58adec 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt @@ -41,9 +41,9 @@ import kotlin.reflect.KClass * This is a test language that can be used for unit test, where we need a language but do not have * a specific one. */ -open class TestLanguage(namespaceDelimiter: String = "::") : Language() { +open class TestLanguage(final override var namespaceDelimiter: String = "::") : + Language() { override val fileExtensions: List = listOf() - final override val namespaceDelimiter: String override val frontend: KClass = TestLanguageFrontend::class override val compoundAssignmentOperators = setOf("+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "|=", "^=") @@ -61,10 +61,6 @@ open class TestLanguage(namespaceDelimiter: String = "::") : Language Unit): TestLanguageFrontend { + var config = TranslationConfiguration.builder().also(builder).build() + + var ctx: TranslationContext = TranslationContext(config, ScopeManager(), TypeManager()) + return TestLanguageFrontend(ctx = ctx) +} + open class TestLanguageFrontend( namespaceDelimiter: String = "::", language: Language = TestLanguage(namespaceDelimiter), diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt index 9ee1f64b38..cc2a6a5b4e 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt @@ -30,7 +30,6 @@ import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.scopes.Symbol import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression @@ -267,11 +266,8 @@ open class CPPLanguage : templateCall.templateInstantiation = functionTemplateDeclaration val edges = templateCall.templateParameterEdges // Set instantiation propertyEdges - for (instantiationParameter in edges ?: listOf()) { - instantiationParameter.addProperty( - Properties.INSTANTIATION, - TemplateDeclaration.TemplateInitialization.EXPLICIT - ) + for (edge in edges ?: listOf()) { + edge.instantiation = TemplateDeclaration.TemplateInitialization.EXPLICIT } if (functionTemplateDeclaration == null) { diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt index 74451bf7ca..40f9d27928 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt @@ -330,7 +330,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : nonTypeTemplateParamDeclaration.default = defaultExpression defaultExpression?.let { nonTypeTemplateParamDeclaration.addPrevDFG(it) - it.addNextDFG(nonTypeTemplateParamDeclaration) + it.nextDFGEdges.add(nonTypeTemplateParamDeclaration) } } templateDeclaration.addParameter(nonTypeTemplateParamDeclaration) diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt index d2c4cdcb8f..99368dc7f7 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt @@ -27,8 +27,6 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.newConstructExpression import de.fraunhofer.aisec.cpg.graph.newInitializerListExpression import de.fraunhofer.aisec.cpg.graph.newProblemExpression @@ -83,10 +81,7 @@ class InitializerHandler(lang: CXXLanguageFrontend) : for (clause in ctx.clauses) { frontend.expressionHandler.handle(clause)?.let { - val edge = PropertyEdge(expression, it) - edge.addProperty(Properties.INDEX, expression.initializerEdges.size) - - expression.initializerEdges.add(edge) + expression.initializerEdges.add(it) expression.addPrevDFG(it) } } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt index f03dd0146f..47a56ccdb2 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt @@ -27,15 +27,14 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.frontends.cxx.CLanguage -import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage +import de.fraunhofer.aisec.cpg.frontends.cxx.CXXLanguageFrontend import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.ast.AstEdge import de.fraunhofer.aisec.cpg.graph.newLiteral +import de.fraunhofer.aisec.cpg.graph.primitiveType import de.fraunhofer.aisec.cpg.graph.statements.expressions.InitializerListExpression -import de.fraunhofer.aisec.cpg.graph.types.IntegerType -import de.fraunhofer.aisec.cpg.graph.types.NumericType import de.fraunhofer.aisec.cpg.helpers.Benchmark import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.test.* @@ -89,16 +88,7 @@ class PerformanceRegressionTest { val list = InitializerListExpression() for (i in 0 until 50000) { - list.initializerEdges.add( - PropertyEdge( - list, - newLiteral( - i, - IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.UNSIGNED), - null - ) - ) - ) + list.initializerEdges.add(AstEdge(list, newLiteral(i, primitiveType("int"), null))) } decl.initializer = list diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt index 5a1d30aadf..b0bb272d86 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt @@ -33,7 +33,6 @@ import de.fraunhofer.aisec.cpg.graph.SearchModifier.UNIQUE import de.fraunhofer.aisec.cpg.graph.allChildren import de.fraunhofer.aisec.cpg.graph.declarations.ConstructorDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker @@ -111,7 +110,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = ifSimple.thenStatement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifSimple) ) ) @@ -130,7 +129,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = ifSimple, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifSimple.thenStatement) ) ) @@ -162,7 +161,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = ifBranched, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifBranched.thenStatement) ) ) @@ -171,7 +170,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = ifBranched, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(ifBranched.elseStatement) ) ) @@ -184,7 +183,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = ifBranched.thenStatement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifBranched) ) ) @@ -195,7 +194,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = ifBranched.elseStatement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(ifBranched) ) ) @@ -284,7 +283,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.EXITS, n = fs, cr = Connect.SUBTREE, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[1]) ) ) @@ -345,7 +344,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.EXITS, n = fs, cr = Connect.SUBTREE, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[2]) ) ) @@ -406,7 +405,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.EXITS, n = fs, cr = Connect.SUBTREE, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[4]) ) ) @@ -511,7 +510,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = wstat.statement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(wstat) ) ) @@ -523,7 +522,7 @@ internal class EOGTest : BaseTest() { cn = Connect.SUBTREE, en = Util.Edge.EXITS, n = wstat, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[1]) ) ) @@ -556,7 +555,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = dostat, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(dostat.statement) ) ) @@ -577,7 +576,7 @@ internal class EOGTest : BaseTest() { cn = Connect.SUBTREE, en = Util.Edge.EXITS, n = dostat, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[2]) ) ) diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.kt index 578bb0d6d8..b81cd85e79 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.kt @@ -32,7 +32,6 @@ import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression import de.fraunhofer.aisec.cpg.test.* @@ -165,7 +164,7 @@ internal class FunctionPointerTest : BaseTest() { } if (functions.size == 0) { variable.usageEdges - .filter { it.getProperty(Properties.ACCESS) == AccessValues.WRITE } + .filter { it.access == AccessValues.WRITE } .forEach { worklist.push(it.end) } while (!worklist.isEmpty()) { val curr = worklist.pop() diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt index 7256379f70..637c34ed80 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt @@ -28,7 +28,6 @@ package de.fraunhofer.aisec.cpg.enhancements.templates import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.FunctionType import de.fraunhofer.aisec.cpg.graph.types.ObjectType @@ -249,7 +248,7 @@ internal class ClassTemplateTest : BaseTest() { assertEquals(literal3Implicit, templateParameters[2]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpr.templateParameterEdges?.get(2)?.getProperty(Properties.INSTANTIATION) + constructExpr.templateParameterEdges?.get(2)?.instantiation ) assertEquals(pair, constructExpr.instantiates) assertEquals(template, constructExpr.templateInstantiation) @@ -269,16 +268,12 @@ internal class ClassTemplateTest : BaseTest() { assertLocalName("int", constructExpression.templateParameters[0]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParameterEdges - ?.get(0) - ?.getProperty(Properties.INSTANTIATION) + constructExpression.templateParameterEdges?.get(0)?.instantiation ) assertLocalName("int", constructExpression.templateParameters[1]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParameterEdges - ?.get(1) - ?.getProperty(Properties.INSTANTIATION) + constructExpression.templateParameterEdges?.get(1)?.instantiation ) val pairTypeInstantiated = constructExpression.type as ObjectType @@ -374,22 +369,22 @@ internal class ClassTemplateTest : BaseTest() { assertLocalName("int", constructExpr.templateParameters[0]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpr.templateParameterEdges?.get(0)?.getProperty(Properties.INSTANTIATION) + constructExpr.templateParameterEdges?.get(0)?.instantiation ) assertLocalName("int", constructExpr.templateParameters[1]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpr.templateParameterEdges?.get(1)?.getProperty(Properties.INSTANTIATION) + constructExpr.templateParameterEdges?.get(1)?.instantiation ) assertEquals(literal2Implicit, constructExpr.templateParameters[2]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpr.templateParameterEdges?.get(2)?.getProperty(Properties.INSTANTIATION) + constructExpr.templateParameterEdges?.get(2)?.instantiation ) assertEquals(literal2Implicit, constructExpr.templateParameters[3]) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpr.templateParameterEdges?.get(3)?.getProperty(Properties.INSTANTIATION) + constructExpr.templateParameterEdges?.get(3)?.instantiation ) val type = constructExpr.type as ObjectType @@ -431,47 +426,27 @@ internal class ClassTemplateTest : BaseTest() { assertLocalName("int", (constructExpression.templateParameters[0] as TypeExpression).type) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParameterEdges - ?.get(0) - ?.getProperty(Properties.INSTANTIATION) - ) - assertEquals( - 0, - constructExpression.templateParameterEdges?.get(0)?.getProperty(Properties.INDEX) + constructExpression.templateParameterEdges?.get(0)?.instantiation ) + assertEquals(0, constructExpression.templateParameterEdges?.get(0)?.index) assertLocalName("int", (constructExpression.templateParameters[1] as TypeExpression).type) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpression.templateParameterEdges - ?.get(1) - ?.getProperty(Properties.INSTANTIATION) - ) - assertEquals( - 1, - constructExpression.templateParameterEdges?.get(1)?.getProperty(Properties.INDEX) + constructExpression.templateParameterEdges?.get(1)?.instantiation ) + assertEquals(1, constructExpression.templateParameterEdges?.get(1)?.index) assertEquals(literal1, constructExpression.templateParameters[2]) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpression.templateParameterEdges - ?.get(2) - ?.getProperty(Properties.INSTANTIATION) - ) - assertEquals( - 2, - constructExpression.templateParameterEdges?.get(2)?.getProperty(Properties.INDEX) + constructExpression.templateParameterEdges?.get(2)?.instantiation ) + assertEquals(2, constructExpression.templateParameterEdges?.get(2)?.index) assertEquals(literal1, constructExpression.templateParameters[3]) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpression.templateParameterEdges - ?.get(3) - ?.getProperty(Properties.INSTANTIATION) - ) - assertEquals( - 3, - constructExpression.templateParameterEdges?.get(3)?.getProperty(Properties.INDEX) + constructExpression.templateParameterEdges?.get(3)?.instantiation ) + assertEquals(3, constructExpression.templateParameterEdges?.get(3)?.index) // Test Type val type = constructExpression.type as ObjectType diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt index be7dd17c67..3a1b7c3c85 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt @@ -28,7 +28,6 @@ package de.fraunhofer.aisec.cpg.enhancements.templates import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.test.* @@ -70,16 +69,16 @@ internal class FunctionTemplateTest : BaseTest() { ) { assertEquals(2, callFloat3.templateParameters.size) assertEquals(floatType, (callFloat3.templateParameters[0] as TypeExpression).type) - assertEquals(0, callFloat3.templateParameterEdges!![0].getProperty(Properties.INDEX)) + assertEquals(0, callFloat3.templateParameterEdges!![0].index) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - callFloat3.templateParameterEdges!![0].getProperty(Properties.INSTANTIATION) + callFloat3.templateParameterEdges!![0].instantiation ) assertEquals(int3, callFloat3.templateParameters[1]) - assertEquals(1, callFloat3.templateParameterEdges!![1].getProperty(Properties.INDEX)) + assertEquals(1, callFloat3.templateParameterEdges!![1].index) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - callFloat3.templateParameterEdges!![1].getProperty(Properties.INSTANTIATION) + callFloat3.templateParameterEdges!![1].instantiation ) } @@ -516,16 +515,16 @@ internal class FunctionTemplateTest : BaseTest() { assertLocalName("int", callExpression.templateParameters[0]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - callExpression.templateParameterEdges?.get(0)?.getProperty(Properties.INSTANTIATION) + callExpression.templateParameterEdges?.get(0)?.instantiation ) - assertEquals(0, callExpression.templateParameterEdges!![0].getProperty(Properties.INDEX)) + assertEquals(0, callExpression.templateParameterEdges!![0].index) val int5 = findByUniquePredicate(result.literals, Predicate { l: Literal<*> -> l.value == 5 }) assertEquals(int5, callExpression.templateParameters[1]) - assertEquals(1, callExpression.templateParameterEdges!![1].getProperty(Properties.INDEX)) + assertEquals(1, callExpression.templateParameterEdges!![1].index) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - callExpression.templateParameterEdges!![1].getProperty(Properties.INSTANTIATION) + callExpression.templateParameterEdges!![1].instantiation ) } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CDataflowTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CDataflowTest.kt index d7f3e58152..95056da1e2 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CDataflowTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CDataflowTest.kt @@ -27,8 +27,8 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration -import de.fraunhofer.aisec.cpg.graph.edge.Dataflow -import de.fraunhofer.aisec.cpg.graph.edge.PartialDataflowGranularity +import de.fraunhofer.aisec.cpg.graph.edge.flows.Dataflow +import de.fraunhofer.aisec.cpg.graph.edge.flows.PartialDataflowGranularity import de.fraunhofer.aisec.cpg.test.* import java.io.File import kotlin.test.Test diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index d07a097e5e..cb9b1258ca 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -29,7 +29,6 @@ import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.analysis.MultiValueEvaluator import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.FunctionType @@ -1173,7 +1172,7 @@ class GoLanguageFrontendTest : BaseTest() { val funcy = result.calls["funcy"] assertNotNull(funcy) - funcy.invokeEdges.all { it.getProperty(Properties.DYNAMIC_INVOKE) == true } + funcy.invokeEdges.all { it.dynamicInvoke == true } // We should be able to resolve the call from our stored "do" function to funcy assertInvokes(funcy, result.functions["do"]) diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt index e8119feac9..09e970c673 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt @@ -82,7 +82,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : parameter.isVarArgs, rawNode = parameter ) - declaration.addParameter(param) + declaration.parameterEdges += param frontend.scopeManager.addDeclaration(param) } @@ -136,7 +136,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : parameter.isVarArgs, rawNode = parameter ) - functionDeclaration.addParameter(param) + functionDeclaration.parameterEdges += param frontend.processAnnotations(param, parameter) frontend.scopeManager.addDeclaration(param) } diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt index a78c169526..3b39b34428 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt @@ -60,7 +60,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val resolvedType = frontend.getTypeAsGoodAsPossible(parameter.type) val param = newParameterDeclaration(parameter.nameAsString, resolvedType, parameter.isVarArgs) - anonymousFunction.addParameter(param) + anonymousFunction.parameterEdges += param frontend.processAnnotations(param, parameter) frontend.scopeManager.addDeclaration(param) } @@ -837,9 +837,8 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : .implicit(anonymousRecord.name.localName) ctor.arguments.forEachIndexed { i, arg -> - constructorDeclaration.addParameter( + constructorDeclaration.parameterEdges += newParameterDeclaration("arg${i}", arg.type) - ) } anonymousRecord.addConstructor(constructorDeclaration) ctor.anonymousClass = anonymousRecord diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt index e427a59a15..08042255dd 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt @@ -30,8 +30,6 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.allChildren import de.fraunhofer.aisec.cpg.graph.declarations.ConstructorDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.Properties -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference @@ -112,7 +110,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = ifSimple.thenStatement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifSimple) ) ) @@ -131,7 +129,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = ifSimple, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifSimple.thenStatement) ) ) @@ -163,7 +161,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = ifBranched, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifBranched.thenStatement) ) ) @@ -172,7 +170,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = ifBranched, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(ifBranched.elseStatement) ) ) @@ -185,7 +183,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = ifBranched.thenStatement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(ifBranched) ) ) @@ -196,7 +194,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = ifBranched.elseStatement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(ifBranched) ) ) @@ -238,7 +236,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = bo.rhs, cr = Connect.SUBTREE, - props = mutableMapOf(Properties.BRANCH to (bo.operatorCode == "&&")), + branch = (bo.operatorCode == "&&"), refs = listOf(bo.lhs) ) ) @@ -269,7 +267,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = bo, cr = Connect.SUBTREE, - props = mutableMapOf(Properties.BRANCH to (bo.operatorCode != "&&")), + branch = (bo.operatorCode != "&&"), refs = listOf(bo.lhs) ) ) @@ -400,7 +398,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.EXITS, n = fs, cr = Connect.SUBTREE, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[2]) ) ) @@ -473,7 +471,7 @@ internal class EOGTest : BaseTest() { en = Util.Edge.ENTRIES, n = wstat.statement, cr = Connect.NODE, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(wstat) ) ) @@ -485,7 +483,7 @@ internal class EOGTest : BaseTest() { cn = Connect.SUBTREE, en = Util.Edge.EXITS, n = wstat, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[1]) ) ) @@ -518,7 +516,7 @@ internal class EOGTest : BaseTest() { cn = Connect.NODE, en = Util.Edge.EXITS, n = dostat, - props = mutableMapOf(Properties.BRANCH to true), + branch = true, refs = listOf(dostat.statement) ) ) @@ -539,7 +537,7 @@ internal class EOGTest : BaseTest() { cn = Connect.SUBTREE, en = Util.Edge.EXITS, n = dostat, - props = mutableMapOf(Properties.BRANCH to false), + branch = false, refs = listOf(prints[2]) ) ) @@ -759,17 +757,17 @@ internal class EOGTest : BaseTest() { assertNotNull(a) val b = result.refs[{ it.location?.region?.startLine == 7 && it.name.localName == "b" }] assertNotNull(b) - var nextEOG: List> = firstIf.nextEOGEdges + var nextEOG = firstIf.nextEOGEdges assertEquals(2, nextEOG.size) for (edge in nextEOG) { assertEquals(firstIf, edge.start) if (edge.end == b) { - assertEquals(true, edge.getProperty(Properties.BRANCH)) - assertEquals(0, edge.getProperty(Properties.INDEX)) + assertEquals(true, edge.branch) + assertEquals(0, edge.index) } else { assertEquals(a, edge.end) - assertEquals(false, edge.getProperty(Properties.BRANCH)) - assertEquals(1, edge.getProperty(Properties.INDEX)) + assertEquals(false, edge.branch) + assertEquals(1, edge.index) } } val elseIf: IfStatement = @@ -786,12 +784,12 @@ internal class EOGTest : BaseTest() { for (edge in nextEOG) { assertEquals(elseIf, edge.start) if (edge.end == b2) { - assertEquals(true, edge.getProperty(Properties.BRANCH)) - assertEquals(0, edge.getProperty(Properties.INDEX)) + assertEquals(true, edge.branch) + assertEquals(0, edge.index) } else { assertEquals(x, edge.end) - assertEquals(false, edge.getProperty(Properties.BRANCH)) - assertEquals(1, edge.getProperty(Properties.INDEX)) + assertEquals(false, edge.branch) + assertEquals(1, edge.index) } } } diff --git a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt index 7a82e97a2a..5da30ce511 100644 --- a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt +++ b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt @@ -29,7 +29,6 @@ import de.fraunhofer.aisec.cpg.analysis.ValueEvaluator import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.Annotation import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.ObjectType @@ -120,7 +119,7 @@ class PythonFrontendTest : BaseTest() { val edge = callExpression.argumentEdges[1] assertNotNull(edge) - assertEquals("s2", edge.getProperty(Properties.NAME)) + assertEquals("s2", edge.name) val s = bar.parameters.first() assertNotNull(s) diff --git a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt index f584b4ca0c..79c60675a8 100644 --- a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt +++ b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt @@ -519,6 +519,8 @@ class Application : Callable { if (!noDefaultPasses) { translationConfiguration.defaultPasses() + translationConfiguration.registerPass() + translationConfiguration.registerPass() } if (customPasses != "DEFAULT") { val pieces = customPasses.split(",") diff --git a/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt b/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt index fbe9cddb86..bf40cdb2a8 100644 --- a/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt +++ b/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt @@ -28,8 +28,8 @@ package de.fraunhofer.aisec.cpg_vis_neo4j import com.fasterxml.jackson.databind.ObjectMapper import de.fraunhofer.aisec.cpg.TranslationManager import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration -import de.fraunhofer.aisec.cpg.graph.functions import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import java.io.File import java.nio.file.Files @@ -62,6 +62,7 @@ class ApplicationTest { @Test fun testSerializeCpgViaOGM() { val (application, translationResult) = createTranslationResult() + // 22 inferred functions, 1 inferred method, 2 inferred constructors, 11 regular functions assertEquals(36, translationResult.functions.size) diff --git a/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Neo4JTest.kt b/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Neo4JTest.kt index efedc540d8..96f477d58b 100644 --- a/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Neo4JTest.kt +++ b/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Neo4JTest.kt @@ -28,7 +28,6 @@ package de.fraunhofer.aisec.cpg_vis_neo4j import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.builder.translationResult -import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.ImportDeclaration import de.fraunhofer.aisec.cpg.graph.functions import kotlin.test.Test @@ -47,23 +46,6 @@ class Neo4JTest { assertEquals(36, translationResult.functions.size) application.pushToNeo4j(translationResult) - - val sessionAndSessionFactoryPair = application.connect() - - val session = sessionAndSessionFactoryPair.first - session.beginTransaction().use { transaction -> - val functions = session.loadAll(FunctionDeclaration::class.java) - assertNotNull(functions) - - // 22 inferred functions, 1 inferred method, 2 inferred constructors, 11 regular - // functions - assertEquals(36, functions.size) - - transaction.commit() - } - - session.clear() - sessionAndSessionFactoryPair.second.close() } @Test