Skip to content

Commit

Permalink
Merge branch 'main' into ak/slice-all-collections
Browse files Browse the repository at this point in the history
  • Loading branch information
KuechA authored Oct 18, 2024
2 parents 3c919ed + 39ef4cf commit 9fc31e8
Show file tree
Hide file tree
Showing 19 changed files with 934 additions and 398 deletions.
68 changes: 4 additions & 64 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -468,71 +468,11 @@ class ScopeManager : ScopeProvider {
}

/**
* This function SHOULD only be used by the
* [de.fraunhofer.aisec.cpg.passes.EvaluationOrderGraphPass] while building up the EOG. It adds
* a [BreakStatement] to the list of break statements of the current "breakable" scope.
* This function retrieves the [LabelStatement] associated with the [labelString]. This depicts
* the feature of some languages to attach a label to a point in the source code and use it as
* the target for control flow manipulation, e.g. [BreakStatement], [GotoStatement].
*/
fun addBreakStatement(breakStatement: BreakStatement) {
if (breakStatement.label == null) {
val scope = firstScopeOrNull { scope: Scope? -> scope?.isBreakable() == true }
if (scope == null) {
Util.errorWithFileLocation(
breakStatement,
LOGGER,
"Break inside of unbreakable scope. The break will be ignored, but may lead " +
"to an incorrect graph. The source code is not valid or incomplete."
)
return
}
(scope as Breakable).addBreakStatement(breakStatement)
} else {
val labelStatement = getLabelStatement(breakStatement.label)
labelStatement?.subStatement?.let {
val scope = lookupScope(it)
(scope as Breakable?)?.addBreakStatement(breakStatement)
}
}
}

/**
* This function SHOULD only be used by the
* [de.fraunhofer.aisec.cpg.passes.EvaluationOrderGraphPass] while building up the EOG. It adds
* a [ContinueStatement] to the list of continue statements of the current "continuable" scope.
*/
fun addContinueStatement(continueStatement: ContinueStatement) {
if (continueStatement.label == null) {
val scope = firstScopeOrNull { scope: Scope? -> scope?.isContinuable() == true }
if (scope == null) {
LOGGER.error(
"Continue inside of not continuable scope. The continue will be ignored, but may lead " +
"to an incorrect graph. The source code is not valid or incomplete."
)
return
}
(scope as Continuable).addContinueStatement(continueStatement)
} else {
val labelStatement = getLabelStatement(continueStatement.label)
labelStatement?.subStatement?.let {
val scope = lookupScope(it)
(scope as Continuable?)?.addContinueStatement(continueStatement)
}
}
}

/**
* This function SHOULD only be used by the
* [de.fraunhofer.aisec.cpg.passes.EvaluationOrderGraphPass] while building up the EOG. It adds
* a [LabelStatement] to the list of label statements of the current scope.
*/
fun addLabelStatement(labelStatement: LabelStatement) {
currentScope?.addLabelStatement(labelStatement)
}

/**
* This function is internal to the scope manager and primarily used by [addBreakStatement] and
* [addContinueStatement]. It retrieves the [LabelStatement] associated with the [labelString].
*/
private fun getLabelStatement(labelString: String?): LabelStatement? {
fun getLabelStatement(labelString: String?): LabelStatement? {
if (labelString == null) return null
var labelStatement: LabelStatement?
var searchScope = currentScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,10 @@ val Node?.forLoops: List<ForStatement>
val Node?.trys: List<TryStatement>
get() = this.allChildren()

/** Returns all [ThrowStatement] child edges in this graph, starting with this [Node]. */
val Node?.throws: List<ThrowStatement>
get() = this.allChildren()

/** Returns all [ForEachStatement] child edges in this graph, starting with this [Node]. */
val Node?.forEachLoops: List<ForEachStatement>
get() = this.allChildren()
Expand Down Expand Up @@ -612,6 +616,31 @@ val Node?.returns: List<ReturnStatement>
val Node?.assigns: List<AssignExpression>
get() = this.allChildren()

/**
* This function tries to find the first parent node that satisfies the condition specified in
* [predicate]. It starts searching in the [searchNode], moving up-wards using the [Node.astParent]
* attribute.
*
* @param searchNode the child node that we start the search from
* @param predicate the search predicate
*/
fun Node.firstParentOrNull(predicate: (Node) -> Boolean): Node? {

// start at searchNodes parent
var node: Node? = this.astParent

while (node != null) {
if (predicate(node)) {
return node
}

// go up-wards in the ast tree
node = node.astParent
}

return null
}

/**
* Return all [ProblemNode] children in this graph (either stored directly or in
* [Node.additionalProblems]), starting with this [Node].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,18 @@ fun MetadataProvider.newLookupScopeStatement(
log(node)
return node
}

/**
* Creates a new [ThrowStatement]. The [MetadataProvider] receiver will be used to fill different
* meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin requires
* an appropriate [MetadataProvider], such as a [LanguageFrontend] as an additional prepended
* argument.
*/
@JvmOverloads
fun MetadataProvider.newThrowStatement(rawNode: Any? = null): ThrowStatement {
val node = ThrowStatement()
node.applyMetadata(this, EMPTY_NAME, rawNode, true)

log(node)
return node
}
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,7 @@ infix fun Expression.assignAsExpr(rhs: Expression): AssignExpression {

return node
}

/**
* Creates a new [AssignExpression] with a `=` [AssignExpression.operatorCode] in the Fluent Node
* DSL and adds it to the nearest enclosing [StatementHolder].
Expand All @@ -1453,6 +1454,23 @@ infix fun Expression.assignAsExpr(rhs: AssignExpression.() -> Unit): AssignExpre
return node
}

/**
* Creates a new [ThrowStatement] in the Fluent Node DSL and adds it to the nearest enclosing
* [StatementHolder].
*/
context(LanguageFrontend<*, *>, Holder<out Node>)
infix fun Expression.`throw`(init: (ThrowStatement.() -> Unit)?): ThrowStatement {
val node = (this@LanguageFrontend).newThrowStatement()
if (init != null) init(node)

val holder = this@Holder
if (holder is StatementHolder) {
holder += node
}

return node
}

/** Creates a new [Type] with the given [name] in the Fluent Node DSL. */
fun LanguageFrontend<*, *>.t(name: CharSequence, generics: List<Type> = listOf()) =
objectType(name, generics)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
*/
package de.fraunhofer.aisec.cpg.graph.scopes

import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.types.Type

class FunctionScope(astNode: FunctionDeclaration) : ValueDeclarationScope(astNode) {
@Transient var catchesOrRelays = mutableMapOf<Type, MutableList<Node>>()
}
class FunctionScope(astNode: FunctionDeclaration) : ValueDeclarationScope(astNode) {}
Original file line number Diff line number Diff line change
Expand Up @@ -25,70 +25,11 @@
*/
package de.fraunhofer.aisec.cpg.graph.scopes

import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.statements.*
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
import org.slf4j.LoggerFactory

class LoopScope(loopStatement: Statement) :
ValueDeclarationScope(loopStatement), Breakable, Continuable {
/**
* Statements that constitute the start of the Loop depending on the used pass, mostly of size 1
*/
val starts: List<Node>
get() =
when (val loopStatement = astNode) {
is WhileStatement -> {
if (loopStatement.conditionDeclaration != null)
SubgraphWalker.getEOGPathEdges(loopStatement.conditionDeclaration).entries
else if (loopStatement.condition != null)
SubgraphWalker.getEOGPathEdges(loopStatement.condition).entries
else SubgraphWalker.getEOGPathEdges(loopStatement.statement).entries
}
is ForStatement -> {
if (loopStatement.conditionDeclaration != null)
SubgraphWalker.getEOGPathEdges(loopStatement.conditionDeclaration).entries
else if (loopStatement.condition != null)
SubgraphWalker.getEOGPathEdges(loopStatement.condition).entries
else SubgraphWalker.getEOGPathEdges(loopStatement.statement).entries
}
is ForEachStatement -> {
SubgraphWalker.getEOGPathEdges(loopStatement).entries
}
is DoStatement -> {
SubgraphWalker.getEOGPathEdges(loopStatement.statement).entries
}
else -> {
LOGGER.error(
"Currently the component {} is not supported as loop scope.",
astNode?.javaClass
)
ArrayList()
}
}

/** Statements that constitute the start of the Loop condition evaluation, mostly of size 1 */
val conditions: List<Node>
get() =
when (val node = astNode) {
is WhileStatement ->
mutableListOf(node.condition, node.conditionDeclaration).filterNotNull()
is ForStatement -> mutableListOf(node.condition).filterNotNull()
is ForEachStatement -> mutableListOf(node.variable).filterNotNull()
is DoStatement -> mutableListOf(node.condition).filterNotNull()
is AssertStatement -> mutableListOf(node.condition).filterNotNull()
null -> {
LOGGER.error("Ast node of loop scope is null.")
mutableListOf()
}
else -> {
LOGGER.error(
"Currently the component {} is not supported as loop scope.",
node.javaClass
)
mutableListOf()
}
}

private val breaks = mutableListOf<BreakStatement>()
private val continues = mutableListOf<ContinueStatement>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ package de.fraunhofer.aisec.cpg.graph.scopes

import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.statements.BreakStatement
import de.fraunhofer.aisec.cpg.graph.types.Type

class TryScope(astNode: Node?) : ValueDeclarationScope(astNode), Breakable {
@Transient val catchesOrRelays = mutableMapOf<Type, MutableList<Node>>()

private val breaks = mutableListOf<BreakStatement>()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.statements

import de.fraunhofer.aisec.cpg.graph.ArgumentHolder
import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf
import de.fraunhofer.aisec.cpg.graph.edges.unwrapping
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import java.util.Objects
import org.apache.commons.lang3.builder.ToStringBuilder
import org.neo4j.ogm.annotation.Relationship

/** Represents a `throw` or `raise` statement. */
class ThrowStatement : Statement(), ArgumentHolder {

/** The exception object to be raised. */
@Relationship(value = "EXCEPTION") var exceptionEdge = astOptionalEdgeOf<Expression>()
var exception by unwrapping(ThrowStatement::exceptionEdge)

/**
* Some languages (Python) can add a parent exception (or `cause`) to indicate that an exception
* was raised while handling another exception.
*/
@Relationship(value = "PARENT_EXCEPTION")
var parentExceptionEdge = astOptionalEdgeOf<Expression>()
var parentException by unwrapping(ThrowStatement::parentExceptionEdge)

override fun addArgument(expression: Expression) {
when {
exception == null -> exception = expression
parentException == null -> parentException = expression
}
}

override fun replaceArgument(old: Expression, new: Expression): Boolean {
return when {
exception == old -> {
exception = new
true
}
parentException == old -> {
parentException = new
true
}
else -> false
}
}

override fun hasArgument(expression: Expression): Boolean {
return exception == expression || parentException == expression
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ThrowStatement) return false
return super.equals(other) &&
exception == other.exception &&
parentException == other.parentException
}

override fun hashCode() = Objects.hash(super.hashCode(), exception, parentException)

override fun toString(): String {
return ToStringBuilder(this, TO_STRING_STYLE)
.appendSuper(super.toString())
.append("exception", exception)
.append("parentException", parentException)
.toString()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) {
is ForStatement -> handleForStatement(node)
is SwitchStatement -> handleSwitchStatement(node)
is IfStatement -> handleIfStatement(node)
is ThrowStatement -> handleThrowStatement(node)
// Declarations
is FieldDeclaration -> handleFieldDeclaration(node)
is FunctionDeclaration -> handleFunctionDeclaration(node, functionSummaries)
Expand Down Expand Up @@ -172,6 +173,12 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) {
comprehension.predicate?.let { comprehension.prevDFG += it }
}

/** Handle a [ThrowStatement]. The exception and parent exception flow into the node. */
protected fun handleThrowStatement(node: ThrowStatement) {
node.exception?.let { node.prevDFGEdges += it }
node.parentException?.let { node.prevDFGEdges += it }
}

protected fun handleAssignExpression(node: AssignExpression) {
// If this is a compound assign, we also need to model a dataflow to the node itself
if (node.isCompoundAssignment) {
Expand Down
Loading

0 comments on commit 9fc31e8

Please sign in to comment.