Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*/
package de.fraunhofer.aisec.cpg.evaluation

import de.fraunhofer.aisec.cpg.graph.DeclarationHolder
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.Field
import de.fraunhofer.aisec.cpg.graph.declarations.Variable
Expand Down Expand Up @@ -297,8 +298,11 @@ class MultiValueEvaluator : ValueEvaluator() {
if (loop == null || loop.condition !is BinaryOperator) return setOf()

var loopVar: Any? =
evaluateInternal(loop.initializerStatement?.declarations?.first(), depth) as? Number
?: return setOf()
evaluateInternal(
(loop.initializerStatement as? DeclarationHolder)?.declarations?.first(),
depth,
)
as? Number ?: return setOf()

val cond = loop.condition as BinaryOperator
val result = mutableSetOf<Any?>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,63 +31,63 @@ import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge
import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdges
import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeList

interface DeclarationHolder {
/**
* Adds the specified declaration to this declaration holder. Ideally, the declaration holder
* should use the [addIfNotContains] method to consistently add declarations.
*
* @param declaration the declaration
*/
fun addDeclaration(declaration: Declaration)
fun <T> addIfNotContains(collection: MutableCollection<T>, declaration: T) {
if (!collection.contains(declaration)) {
collection.add(declaration)
}
}

fun <T : AstNode, P : AstEdge<T>> addIfNotContains(collection: AstEdges<T, P>, declaration: T) {
addIfNotContains(collection, declaration, true)
}

fun <T : AstNode, P : Edge<T>> addIfNotContains(collection: EdgeList<T, P>, declaration: T) {
addIfNotContains(collection, declaration, true)
}

fun <T : Declaration> addIfNotContains(collection: MutableCollection<T>, declaration: T) {
if (!collection.contains(declaration)) {
collection.add(declaration)
/**
* Adds a declaration to a collection of property edges, which contain the declarations
*
* @param collection the collection
* @param declaration the declaration
* @param <T> the type of the declaration
* @param outgoing whether the property is outgoing </T>
*/
fun <T : Node, P : Edge<T>> addIfNotContains(
collection: EdgeList<T, out P>,
declaration: T,
outgoing: Boolean,
) {
var contains = false
for (element in collection) {
if (outgoing) {
if (element.end == declaration) {
contains = true
break
}
} else {
if (element.start == declaration) {
contains = true
break
}
}
}

fun <T : AstNode, P : AstEdge<T>> addIfNotContains(collection: AstEdges<T, P>, declaration: T) {
addIfNotContains(collection, declaration, true)
if (contains) {
return
}

fun <T : AstNode, P : Edge<T>> addIfNotContains(collection: EdgeList<T, P>, declaration: T) {
addIfNotContains(collection, declaration, true)
}
collection.add(declaration)
}

interface DeclarationHolder {
/**
* Adds a declaration to a collection of property edges, which contain the declarations
* Adds the specified declaration to this declaration holder. Ideally, the declaration holder
* should use the [addIfNotContains] method to consistently add declarations.
*
* @param collection the collection
* @param declaration the declaration
* @param <T> the type of the declaration
* @param outgoing whether the property is outgoing </T>
*/
fun <T : Node, P : Edge<T>> addIfNotContains(
collection: EdgeList<T, out P>,
declaration: T,
outgoing: Boolean,
) {
var contains = false
for (element in collection) {
if (outgoing) {
if (element.end == declaration) {
contains = true
break
}
} else {
if (element.start == declaration) {
contains = true
break
}
}
}

if (contains) {
return
}

collection.add(declaration)
}
fun addDeclaration(declaration: Declaration)

val declarations: List<Declaration>
}
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,6 @@ fun <T> Literal<T>.duplicate(implicit: Boolean): Literal<T> {
duplicate.assignedTypes = this.assignedTypes
duplicate.code = this.code
duplicate.location = this.location
duplicate.locals = this.locals
duplicate.argumentIndex = this.argumentIndex
duplicate.annotations = this.annotations
duplicate.comment = this.comment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,12 @@ class Context(
var steps: Int = 0,
) : HasAssumptions {

override val assumptions: MutableSet<Assumption> = mutableSetOf()
private var assumptionsStorage: MutableSet<Assumption>? = null

override val assumptions: MutableSet<Assumption>
get() {
return assumptionsStorage ?: mutableSetOf<Assumption>().also { assumptionsStorage = it }
}

fun clone(): Context {
return Context(indexStack = indexStack.clone(), callStack = callStack.clone(), steps)
Expand Down Expand Up @@ -1580,13 +1585,12 @@ inline fun <reified T : Scope> Scope.firstScopeParentOrNull(
*/
val AstNode?.problems: List<ProblemNode>
get() {
val relevantNodes =
this.allChildren<Node> { it is ProblemNode || it.additionalProblems.isNotEmpty() }
val relevantNodes = this.allChildren<Node> { it is ProblemNode || it.hasAdditionalProblems }

val result = mutableListOf<ProblemNode>()

relevantNodes.forEach {
if (it.additionalProblems.isNotEmpty()) {
if (it.hasAdditionalProblems) {
result += it.additionalProblems
}
if (it is ProblemNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ import de.fraunhofer.aisec.cpg.PopulatedByPass
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.graph.declarations.Method
import de.fraunhofer.aisec.cpg.graph.declarations.Operator
import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.Variable
import de.fraunhofer.aisec.cpg.graph.edges.MemoryAddressEdges
import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge
import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdges
import de.fraunhofer.aisec.cpg.graph.edges.flows.Dataflows
import de.fraunhofer.aisec.cpg.graph.edges.flows.FullDataflowGranularity
import de.fraunhofer.aisec.cpg.graph.expressions.BinaryOperator
Expand All @@ -45,8 +48,22 @@ import de.fraunhofer.aisec.cpg.passes.DFGPass
import de.fraunhofer.aisec.cpg.passes.PointsToPass
import de.fraunhofer.aisec.cpg.passes.SymbolResolver
import de.fraunhofer.aisec.cpg.persistence.DoNotPersist
import de.fraunhofer.aisec.cpg.persistence.Relationship
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation

interface HasLocals {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs kdoc

/**
* A list of local variables (or other values) associated to this statement, defined by their
* [ValueDeclaration] extracted from Block because `for`, `while`, `if`, and `switch` can
* declare locals in their condition or initializers.
*/
@Relationship(value = "LOCALS", direction = Relationship.Direction.OUTGOING)
var localEdges: AstEdges<ValueDeclaration, AstEdge<ValueDeclaration>>

/** Virtual property to access [localEdges] without property edges. */
var locals: MutableList<ValueDeclaration>
}

/**
* Represents that this node (potentially) makes use of the given memory addresses e.g. to load or
* store data.
Expand Down
25 changes: 17 additions & 8 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Name.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,16 @@ class Name(
* this is basically a cache for [toString]. Otherwise, we would need to call [toString] a lot
* of times, to implement the necessary functions for [CharSequence].
*/
private val fullName: String by lazy {
(if (parent != null) parent.toString() + delimiter else "") + localName
private var qualifiedFullNameCache: String? = null

private fun fullName(): String {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a function now instead of a property? that changes things on many places in the code unnecessary and might even impact serialization.

Can't you just use val fullName { get() { lazy { .... } } which basically does what you want.

// Most names are unqualified, so avoid any extra cache object/value in this hot case.
if (parent == null) {
return localName
}

return qualifiedFullNameCache
?: (parent.toString() + delimiter + localName).also { qualifiedFullNameCache = it }
}

public override fun clone(): Name = Name(localName, parent?.clone(), delimiter)
Expand All @@ -93,13 +101,14 @@ class Name(
* Returns the string representation of this name using a fully qualified name notation with the
* specified [delimiter].
*/
override fun toString() = fullName
override fun toString() = fullName()

override val length = fullName.length
override val length: Int
get() = fullName().length

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is String) return this.fullName == other
if (other is String) return this.toString() == other
if (other is Name)
return localName == other.localName &&
parent == other.parent &&
Expand All @@ -108,12 +117,12 @@ class Name(
return false
}

override fun get(index: Int) = fullName[index]
override fun get(index: Int) = fullName()[index]

override fun hashCode() = Objects.hash(localName, parent, delimiter)

override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
fullName.subSequence(startIndex, endIndex)
fullName().subSequence(startIndex, endIndex)

/**
* Determines if this name ends with the [ending] (i.e., the localNames match until the [ending]
Expand Down Expand Up @@ -141,7 +150,7 @@ class Name(
Name(localName.replace(oldValue, newValue), parent, delimiter)

/** Compare names according to the string representation of the [fullName]. */
override fun compareTo(other: Name) = fullName.compareTo(other.toString())
override fun compareTo(other: Name) = toString().compareTo(other.toString())

/**
* A name can be considered as "qualified", if it has any specified [parent]. Otherwise, only
Expand Down
51 changes: 38 additions & 13 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 @@ -111,21 +111,25 @@ abstract class Node() :
@Relationship(value = "EOG", direction = Relationship.Direction.INCOMING)
@PopulatedByPass(EvaluationOrderGraphPass::class)
var prevEOGEdges: EvaluationOrders<Node> =
EvaluationOrders<Node>(this, mirrorProperty = Node::nextEOGEdges, outgoing = false)
EvaluationOrders<Node>(this, mirroredCollection = { it.nextEOGEdges }, outgoing = false)
protected set

/** Outgoing control flow edges. */
@Relationship(value = "EOG", direction = Relationship.Direction.OUTGOING)
@PopulatedByPass(EvaluationOrderGraphPass::class)
var nextEOGEdges: EvaluationOrders<Node> =
EvaluationOrders<Node>(this, mirrorProperty = Node::prevEOGEdges, outgoing = true)
EvaluationOrders<Node>(this, mirroredCollection = { it.prevEOGEdges }, outgoing = true)
protected set

/** The [BasicBlockEdgeList] leading to the basic block this node belongs to. */
@Relationship(value = "BB", direction = Relationship.Direction.OUTGOING)
@PopulatedByPass(BasicBlockCollectorPass::class)
var basicBlockEdges: BasicBlockEdgeList<Node> =
BasicBlockEdgeList<Node>(this, mirrorProperty = BasicBlock::nodeEdges, outgoing = true)
BasicBlockEdgeList<Node>(
this,
mirroredCollection = { (it as BasicBlock).nodeEdges },
outgoing = true,
)
protected set

/** The basic block this node belongs to. */
Expand All @@ -138,7 +142,7 @@ abstract class Node() :
@PopulatedByPass(ControlDependenceGraphPass::class)
@Relationship(value = "CDG", direction = Relationship.Direction.OUTGOING)
var nextCDGEdges: ControlDependences<Node> =
ControlDependences(this, mirrorProperty = Node::prevCDGEdges, outgoing = true)
ControlDependences(this, mirroredCollection = { it.prevCDGEdges }, outgoing = true)
protected set

var nextCDG by unwrapping(Node::nextCDGEdges)
Expand All @@ -150,7 +154,7 @@ abstract class Node() :
@PopulatedByPass(ControlDependenceGraphPass::class)
@Relationship(value = "CDG", direction = Relationship.Direction.INCOMING)
var prevCDGEdges: ControlDependences<Node> =
ControlDependences<Node>(this, mirrorProperty = Node::nextCDGEdges, outgoing = false)
ControlDependences<Node>(this, mirroredCollection = { it.nextCDGEdges }, outgoing = false)
protected set

var prevCDG by unwrapping(Node::prevCDGEdges)
Expand All @@ -167,7 +171,7 @@ abstract class Node() :
@Relationship(value = "DFG", direction = Relationship.Direction.INCOMING)
@PopulatedByPass(DFGPass::class, PointsToPass::class)
var prevDFGEdges: Dataflows<Node> =
Dataflows<Node>(this, mirrorProperty = Node::nextDFGEdges, outgoing = false)
Dataflows<Node>(this, mirroredCollection = { it.nextDFGEdges }, outgoing = false)
protected set

/** Virtual property for accessing [prevDFGEdges] without property edges. */
Expand Down Expand Up @@ -200,7 +204,7 @@ abstract class Node() :
@PopulatedByPass(DFGPass::class, PointsToPass::class)
@Relationship(value = "DFG", direction = Relationship.Direction.OUTGOING)
var nextDFGEdges: Dataflows<Node> =
Dataflows<Node>(this, mirrorProperty = Node::prevDFGEdges, outgoing = true)
Dataflows<Node>(this, mirroredCollection = { it.prevDFGEdges }, outgoing = true)
protected set

/** Virtual property for accessing [nextDFGEdges] without property edges. */
Expand Down Expand Up @@ -233,7 +237,7 @@ abstract class Node() :
@PopulatedByPass(ProgramDependenceGraphPass::class)
@Relationship(value = "PDG", direction = Relationship.Direction.OUTGOING)
var nextPDGEdges: ProgramDependences<Node> =
ProgramDependences<Node>(this, mirrorProperty = Node::prevPDGEdges, outgoing = true)
ProgramDependences<Node>(this, mirroredCollection = { it.prevPDGEdges }, outgoing = true)
protected set

var nextPDG by unwrapping(Node::nextPDGEdges)
Expand All @@ -242,12 +246,18 @@ abstract class Node() :
@PopulatedByPass(ProgramDependenceGraphPass::class)
@Relationship(value = "PDG", direction = Relationship.Direction.INCOMING)
var prevPDGEdges: ProgramDependences<Node> =
ProgramDependences<Node>(this, mirrorProperty = Node::nextPDGEdges, outgoing = false)
ProgramDependences<Node>(this, mirroredCollection = { it.nextPDGEdges }, outgoing = false)
protected set

var prevPDG by unwrapping(Node::prevPDGEdges)

@DoNotPersist override val assumptions: MutableSet<Assumption> = mutableSetOf()
@DoNotPersist @JsonIgnore private var assumptionsStorage: MutableSet<Assumption>? = null

@DoNotPersist
override val assumptions: MutableSet<Assumption>
get() {
return assumptionsStorage ?: mutableSetOf<Assumption>().also { assumptionsStorage = it }
}

/**
* If a node is marked as being inferred, it means that it was created artificially and does not
Expand Down Expand Up @@ -288,19 +298,34 @@ abstract class Node() :
* Additional problem nodes. These nodes represent problems which occurred during processing of
* a node (i.e. only partially processed).
*/
val additionalProblems: MutableSet<ProblemNode> = mutableSetOf()
@DoNotPersist @JsonIgnore private var additionalProblemsStorage: MutableSet<ProblemNode>? = null

val additionalProblems: MutableSet<ProblemNode>
get() {
return additionalProblemsStorage
?: mutableSetOf<ProblemNode>().also { additionalProblemsStorage = it }
}

@DoNotPersist
val hasAdditionalProblems: Boolean
get() = additionalProblemsStorage?.isNotEmpty() == true

@Relationship(value = "OVERLAY", direction = Relationship.Direction.OUTGOING)
val overlayEdges: Overlays =
Overlays(this, mirrorProperty = OverlayNode::underlyingNodeEdge, outgoing = true)
Overlays(
this,
mirroredCollection = { (it as OverlayNode).underlyingNodeEdge },
outgoing = true,
)
var overlays by unwrapping(Node::overlayEdges)

/**
* Adds the [assumptions] attached to the [Node] and of relevant supernodes in the AST.
* Currently, of the [Component].
*/
override fun relevantAssumptions(): Set<Assumption> {
return super.relevantAssumptions() + (component?.relevantAssumptions() ?: emptySet())
val localAssumptions = assumptionsStorage?.toSet() ?: emptySet()
return localAssumptions + (component?.relevantAssumptions() ?: emptySet())
}

/**
Expand Down
Loading
Loading