Skip to content

Commit

Permalink
Scopes are now nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Dec 11, 2024
1 parent 25c5334 commit 575af42
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import org.slf4j.LoggerFactory
/** The base class for all graph objects that are going to be persisted in the database. */
abstract class Node :
IVisitable<Node>,
PersistedAsNode,
Persistable,
LanguageProvider,
ScopeProvider,
ContextProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,5 @@
*/
package de.fraunhofer.aisec.cpg.graph

import de.fraunhofer.aisec.cpg.graph.scopes.Scope

/**
* This interface represents that the object will be persisted as a "node" in a graph database. This
* is not to be confused with our [Node] class -- for example a [Scope] is also a [PersistedAsNode],
* but not a [Node].
*/
interface PersistedAsNode : Persistable

/** This interface represents that the object will be persisted as a "edge" in a graph database. */
interface PersistedAsEdge : Persistable

/** This interface represents all objects that can be persisted in a graph database. */
interface Persistable
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ 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.PersistedAsEdge
import de.fraunhofer.aisec.cpg.graph.Persistable
import de.fraunhofer.aisec.cpg.graph.edges.flows.DependenceType
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.passes.ProgramDependenceGraphPass
Expand All @@ -51,7 +51,7 @@ import org.neo4j.ogm.annotation.*
* ```
*/
@RelationshipEntity
abstract class Edge<NodeType : Node> : PersistedAsEdge, Cloneable {
abstract class Edge<NodeType : Node> : Persistable, Cloneable {
/** Required field for object graph mapping. It contains the node id. */
@field:Id @field:GeneratedValue private val id: Long? = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*/
package de.fraunhofer.aisec.cpg.graph.scopes

import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.Node

/**
Expand All @@ -37,6 +38,6 @@ sealed class NameScope(node: Node?) : StructureDeclarationScope(node) {
init {
astNode = node
// Set the name so that we can use it as a namespace later
name = node?.name
name = node?.name ?: Name(EMPTY_NAME)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,16 @@ package de.fraunhofer.aisec.cpg.graph.scopes

import com.fasterxml.jackson.annotation.JsonBackReference
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.Node.Companion.TO_STRING_STYLE
import de.fraunhofer.aisec.cpg.graph.PersistedAsNode
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.ImportDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.LabelStatement
import de.fraunhofer.aisec.cpg.graph.statements.LookupScopeStatement
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference
import de.fraunhofer.aisec.cpg.helpers.neo4j.NameConverter
import kotlin.uuid.Uuid
import org.apache.commons.lang3.builder.ToStringBuilder
import org.neo4j.ogm.annotation.GeneratedValue
import org.neo4j.ogm.annotation.Id
import org.neo4j.ogm.annotation.NodeEntity
import org.neo4j.ogm.annotation.Relationship
import org.neo4j.ogm.annotation.typeconversion.Convert

/**
* A symbol is a simple, local name. It is valid within the scope that declares it and all of its
Expand All @@ -62,19 +55,11 @@ sealed class Scope(
@Relationship(value = "SCOPE", direction = Relationship.Direction.INCOMING)
@JsonBackReference
open var astNode: Node?
) : PersistedAsNode {

/** Required field for object graph mapping. It contains the scope id. */
@Id @GeneratedValue var legacyId: Long? = null

var id: Uuid = Uuid.random()
) : Node() {

/** FQN Name currently valid */
var scopedName: String? = null

/** The real new name */
@Convert(NameConverter::class) var name: Name? = null

/**
* Scopes are nested and therefore have a parent child relationship, this two members will help
* navigate through the scopes,e.g. when looking up variables.
Expand Down
65 changes: 46 additions & 19 deletions cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,22 @@ import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.Persistable
import de.fraunhofer.aisec.cpg.graph.PersistedAsNode
import de.fraunhofer.aisec.cpg.graph.edges.Edge
import de.fraunhofer.aisec.cpg.graph.edges.allEdges
import de.fraunhofer.aisec.cpg.graph.edges.flows.DependenceType
import de.fraunhofer.aisec.cpg.graph.edges.flows.Granularity
import de.fraunhofer.aisec.cpg.graph.nodes
import de.fraunhofer.aisec.cpg.graph.types.HasType
import de.fraunhofer.aisec.cpg.graph.types.SecondOrderType
import de.fraunhofer.aisec.cpg.helpers.Benchmark
import de.fraunhofer.aisec.cpg.helpers.neo4j.DataflowGranularityConverter
import de.fraunhofer.aisec.cpg.helpers.neo4j.NameConverter
import de.fraunhofer.aisec.cpg.helpers.neo4j.SimpleNameConverter
import kotlin.collections.plusAssign
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.createType
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.superclasses
import kotlin.reflect.full.withNullability
import kotlin.uuid.Uuid
import org.neo4j.driver.GraphDatabase
Expand All @@ -63,7 +64,7 @@ val neo4jSession by lazy {
driver.session()
}

val labelCache: MutableMap<KClass<out PersistedAsNode>, Set<String>> = mutableMapOf()
val labelCache: MutableMap<KClass<out Node>, Set<String>> = mutableMapOf()

val schemaPropertiesCache:
MutableMap<KClass<out Persistable>, Map<String, KProperty1<out Persistable, *>>> =
Expand All @@ -75,32 +76,36 @@ val edgeChunkSize = 10000
val nodeChunkSize = 10000

fun TranslationResult.persist() {
val nodes = this@persist.nodes
val edges = this@persist.allEdges<Edge<*>>()
val b = Benchmark(Persistable::class.java, "Persisting translation result")

val astNodes = this@persist.nodes
val scopes = this.finalCtx.scopeManager.filterScopes { true }
val languages = this.finalCtx.config.languages
val types =
this.finalCtx.typeManager.firstOrderTypes + this.finalCtx.typeManager.secondOrderTypes
val nodes = listOf(astNodes, scopes, languages, types).flatten()
val edges = this@persist.allEdges<Edge<*>>()

val b = Benchmark(Persistable::class.java, "Persisting translation result")

log.info("Persisting {} AST nodes", nodes.size)
log.info(
"Persisting {} nodes: AST nodes ({}), types ({}), scopes ({}) and languages ({})",
nodes.size,
astNodes.size,
types.size,
scopes.size,
languages.size
)
nodes.persist()

log.info("Persisting {} scopes", nodes.size)
scopes.persist()

log.info("Persisting {} languages", nodes.size)
languages.persist()

log.info("Persisting {} edges", edges.size)
edges.persist()

log.info("Persisting {} extra relationships (language, scopes)", edges.size)
log.info("Persisting {} extra relationships (types, scopes, languages)", edges.size)
nodes.persistExtraRelationships()

b.stop()
}

private fun List<PersistedAsNode>.persist() {
private fun List<Node>.persist() {
this.chunked(nodeChunkSize).map { chunk ->
val b = Benchmark(Persistable::class.java, "Persisting chunk of ${chunk.size} nodes")
val params =
Expand Down Expand Up @@ -141,9 +146,13 @@ private fun Collection<Edge<*>>.persist() {
}
}

/**
* Some of our relationships are not real "edges" (yet). We need to handle these case separately
* (for now).
*/
private fun List<Node>.persistExtraRelationships() {
this.flatMap {
listOf(
listOfNotNull(
mapOf(
"startId" to it.id.toString(),
"endId" to it.language?.id.toString(),
Expand All @@ -154,6 +163,21 @@ private fun List<Node>.persistExtraRelationships() {
"endId" to it.scope?.id.toString(),
"type" to "SCOPE"
),
if (it is HasType) {
mapOf(
"startId" to it.id.toString(),
"endId" to it.type.id.toString(),
"type" to "TYPE"
)
} else if (it is SecondOrderType) {
mapOf(
"startId" to it.id.toString(),
"endId" to it.elementType.id.toString(),
"type" to "ELEMENT_TYPE"
)
} else {
null
}
)
}
.chunked(10000)
Expand Down Expand Up @@ -224,10 +248,13 @@ fun Any.convert(originalKey: String, properties: MutableMap<String, Any?>) {
}
}

val KClass<out PersistedAsNode>.labels: Set<String>
val KClass<out Node>.labels: Set<String>
get() {
// Check, if we already computed the labels for this node's class
return labelCache.computeIfAbsent(this) { setOf<String>("Node", this.simpleName!!) }
return labelCache.computeIfAbsent(this) {
setOfNotNull<String>("Node", this.simpleName) +
it.superclasses.mapNotNull { it.simpleName }
}
}

val propertyTypes =
Expand Down

0 comments on commit 575af42

Please sign in to comment.