Skip to content

Commit

Permalink
Implemented overlay edge
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Dec 17, 2024
1 parent 67e3a2a commit 21aaf84
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 17 deletions.
10 changes: 10 additions & 0 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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.concepts.OverlaySingleEdge
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.edges.*
import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf
Expand Down Expand Up @@ -274,6 +275,15 @@ abstract class Node :
*/
val additionalProblems: MutableSet<ProblemNode> = mutableSetOf()

val overlayNodeEdge: OverlaySingleEdge =
OverlaySingleEdge(
this,
of = null,
mirrorProperty = OverlayNode::underlyingNodeEdge,
outgoing = true
)
var overlayNode by unwrapping(Node::overlayNodeEdge)

/**
* If a node should be removed from the graph, just removing it from the AST is not enough (see
* issue #60). It will most probably be referenced somewhere via DFG or EOG edges. Thus, if it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,21 @@
*/
package de.fraunhofer.aisec.cpg.graph

import de.fraunhofer.aisec.cpg.graph.concepts.OverlaySingleEdge
import de.fraunhofer.aisec.cpg.graph.edges.unwrapping

/**
* Represents an extra node added to the CPG. These nodes can live next to the CPG, typically having
* shared edges to extend the original CPG graph.
*/
abstract class OverlayNode : Node() {
abstract class OverlayNode() : Node() {
/** All [OverlayNode]s nodes are connected to an original cpg [Node] by this. */
abstract val underlayingNode: Node
val underlyingNodeEdge: OverlaySingleEdge =
OverlaySingleEdge(
this,
of = null,
mirrorProperty = Node::overlayNodeEdge,
outgoing = false,
)
var underlyingNode by unwrapping(OverlayNode::underlyingNodeEdge)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,37 @@
*/
package de.fraunhofer.aisec.cpg.graph.concepts

import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.OverlayNode
import de.fraunhofer.aisec.cpg.graph.edges.Edge
import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeSingletonList
import de.fraunhofer.aisec.cpg.graph.edges.collections.MirroredEdgeCollection
import kotlin.reflect.KProperty

/**
* Represents a new concept added to the CPG. This is intended for modelling "concepts" like
* logging, files, databases. The relevant operations on this concept are modeled as [Operation]s
* and stored in [ops].
*/
abstract class Concept() : OverlayNode() {
abstract class Concept<T : Operation>() : OverlayNode() {
/** All [Operation]s belonging to this concept. */
abstract val ops: Set<Operation>
val ops: Set<T> = setOf()
}

class OverlayEdge(start: Node, end: Node) : Edge<Node>(start, end) {
override var labels: Set<String> = setOf("OVERLAY")
}

class OverlaySingleEdge(
thisRef: Node,
of: Node?,
override var mirrorProperty: KProperty<MutableCollection<OverlayEdge>>,
outgoing: Boolean = true,
) :
EdgeSingletonList<Node, Node?, OverlayEdge>(
thisRef = thisRef,
init = ::OverlayEdge,
outgoing = outgoing,
of = of,
),
MirroredEdgeCollection<Node, OverlayEdge>
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import de.fraunhofer.aisec.cpg.graph.OverlayNode
* Represents an operation executed on/with a [Concept] (stored in [concept]). This is typically a
* `write` on a file or log object or an `execute` on a database.
*/
abstract class Operation : OverlayNode() {
abstract class Operation(
/** The [Concept] this operation belongs to. */
abstract val concept: Concept
}
val concept: Concept<*>
) : OverlayNode()
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ import org.neo4j.ogm.annotation.Transient
*
* Therefore, we need to wrap the edge in a list with a single element.
*/
class EdgeSingletonList<NodeType : Node, NullableNodeType : NodeType?, EdgeType : Edge<NodeType>>(
open class EdgeSingletonList<
NodeType : Node,
NullableNodeType : NodeType?,
EdgeType : Edge<NodeType>
>(
override var thisRef: Node,
override var init: (Node, NodeType) -> EdgeType,
var onChanged: ((old: EdgeType?, new: EdgeType?) -> Unit)? = null,
Expand Down Expand Up @@ -71,7 +75,15 @@ class EdgeSingletonList<NodeType : Node, NullableNodeType : NodeType?, EdgeType
}

override fun add(element: EdgeType): Boolean {
throw UnsupportedOperationException()
if (this.element == null) {
this.element = element
onChanged?.invoke(null, this.element)
return true
} else {
throw UnsupportedOperationException(
"We cannot 'add' to a singleton edge list, that is already populated"
)
}
}

override fun addAll(elements: Collection<EdgeType>): Boolean {
Expand Down Expand Up @@ -151,6 +163,13 @@ class EdgeSingletonList<NodeType : Node, NullableNodeType : NodeType?, EdgeType
@Suppress("UNCHECKED_CAST") init(node, thisRef as NodeType)
}
onChanged?.invoke(old, this.element)

val element = this.element
if (element != null) {
handleOnAdd(element)
} else if (old != null) {
handleOnRemove(old)
}
}

fun <ThisType : Node> delegate(): UnwrapDelegate<ThisType> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ fun TranslationResult.persist() {

val astNodes = this@persist.nodes
val connected = astNodes.flatMap { it.connectedNodes }.toSet()
val nodes = (astNodes + connected).distinct()
val nodes = (astNodes + connected + this.additionalNodes).distinct()

log.info(
"Persisting {} nodes: AST nodes ({}), other nodes ({})",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
package de.fraunhofer.aisec.cpg_vis_neo4j

import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.concepts.Concept
import de.fraunhofer.aisec.cpg.graph.concepts.Operation
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal
import java.math.BigInteger
import kotlin.test.Test
Expand All @@ -37,24 +39,45 @@ import org.junit.jupiter.api.Tag
class Neo4JTest {
@Test
fun testPush() {
val (application, translationResult) = createTranslationResult()
val (application, result) = createTranslationResult()

// 22 inferred functions, 1 inferred method, 2 inferred constructors, 11 regular functions
assertEquals(36, translationResult.functions.size)
assertEquals(36, result.functions.size)

application.pushToNeo4j(translationResult)
application.pushToNeo4j(result)
}

@Test
fun testPushVeryLong() {
val (application, translationResult) = createTranslationResult("very_long.cpp")
val (application, result) = createTranslationResult("very_long.cpp")

assertEquals(1, translationResult.variables.size)
assertEquals(1, result.variables.size)

val lit = translationResult.variables["l"]?.initializer
val lit = result.variables["l"]?.initializer
assertIs<Literal<BigInteger>>(lit)
assertEquals(BigInteger("10958011617037158669"), lit.value)

application.pushToNeo4j(translationResult)
application.pushToNeo4j(result)
}

@Test
fun testPushConcepts() {
val (application, result) = createTranslationResult()

val connectCall = result.calls["connect"]

abstract class NetworkingOperation(
concept: Concept<*>,
) : Operation(concept)
class Connect(concept: Concept<*>) : NetworkingOperation(concept)
class Networking() : Concept<NetworkingOperation>()

val nw = Networking()
nw.underlyingNode = result.translationUnit
val connect = Connect(concept = nw)
connect.underlyingNode = connectCall
assertEquals(connect, connectCall?.overlayNode)

application.pushToNeo4j(result)
}
}

0 comments on commit 21aaf84

Please sign in to comment.