Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ class ScopeManager(override var ctx: TranslationContext) : ScopeProvider, Contex
// also update the AST node of the existing scope to the "latest" we have seen
existing.astNode = entry.value.astNode

// Re-parent the children of the duplicate scope (entry.value) to the surviving
// scope (existing). Without this, inner scopes such as FunctionScopes whose
// parent still points to the stale duplicate scope would miss symbols that are
// only declared in the surviving scope when walking the parent chain during
// symbol resolution.
for (child in entry.value.children) {
child.parent = existing
existing.children.add(child)
}
entry.value.children.clear()

// now it gets more tricky. we also need to "redirect" the AST nodes in the sub
// scope manager to our existing NameScope (currently, they point to their own,
// invalid copy of the NameScope).
Expand Down
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 {
/**
* 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 {
// 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
Loading
Loading