diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt index b49b683752..aa13ec0ded 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt @@ -26,9 +26,11 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators +import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.graph.Node.Companion.EMPTY_NAME import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log import de.fraunhofer.aisec.cpg.graph.edges.flows.ContextSensitiveDataflow +import de.fraunhofer.aisec.cpg.graph.statements.ThrowExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.AssignExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.CollectionComprehension @@ -565,6 +567,21 @@ fun MetadataProvider.newTypeExpression( return node } +/** + * Creates a new [ThrowExpression]. 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.newThrowExpression(rawNode: Any? = null): ThrowExpression { + val node = ThrowExpression() + node.applyMetadata(this, EMPTY_NAME, rawNode, true) + + log(node) + return node +} + /** * Creates a new [ProblemExpression]. The [MetadataProvider] receiver will be used to fill different * meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin requires diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt index fd09e18933..0c9eb26b4b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt @@ -572,8 +572,8 @@ val Node?.forLoops: List val Node?.trys: List get() = this.allChildren() -/** Returns all [ThrowStatement] child edges in this graph, starting with this [Node]. */ -val Node?.throws: List +/** Returns all [ThrowExpression] child edges in this graph, starting with this [Node]. */ +val Node?.throws: List get() = this.allChildren() /** Returns all [ForEachStatement] child edges in this graph, starting with this [Node]. */ diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt index 7abfc4a9e8..b01b6ce5c1 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt @@ -356,18 +356,3 @@ 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 -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt index 7beb6cf634..50cc3c0d32 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt @@ -1457,12 +1457,12 @@ infix fun Expression.assignAsExpr(rhs: AssignExpression.() -> Unit): AssignExpre } /** - * Creates a new [ThrowStatement] in the Fluent Node DSL and adds it to the nearest enclosing + * Creates a new [ThrowExpression] in the Fluent Node DSL and adds it to the nearest enclosing * [StatementHolder]. */ context(LanguageFrontend<*, *>, Holder) -infix fun Expression.`throw`(init: (ThrowStatement.() -> Unit)?): ThrowStatement { - val node = (this@LanguageFrontend).newThrowStatement() +infix fun Expression.`throw`(init: (ThrowExpression.() -> Unit)?): ThrowExpression { + val node = (this@LanguageFrontend).newThrowExpression() if (init != null) init(node) val holder = this@Holder diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ThrowStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ThrowExpression.kt similarity index 91% rename from cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ThrowStatement.kt rename to cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ThrowExpression.kt index 27a8d1b200..0f8b86cb32 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ThrowStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ThrowExpression.kt @@ -33,12 +33,12 @@ 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 { +/** Represents a `throw` or `raise` statement/expression. */ +class ThrowExpression : Expression(), ArgumentHolder { /** The exception object to be raised. */ @Relationship(value = "EXCEPTION") var exceptionEdge = astOptionalEdgeOf() - var exception by unwrapping(ThrowStatement::exceptionEdge) + var exception by unwrapping(ThrowExpression::exceptionEdge) /** * Some languages (Python) can add a parent exception (or `cause`) to indicate that an exception @@ -46,7 +46,7 @@ class ThrowStatement : Statement(), ArgumentHolder { */ @Relationship(value = "PARENT_EXCEPTION") var parentExceptionEdge = astOptionalEdgeOf() - var parentException by unwrapping(ThrowStatement::parentExceptionEdge) + var parentException by unwrapping(ThrowExpression::parentExceptionEdge) override fun addArgument(expression: Expression) { when { @@ -75,7 +75,7 @@ class ThrowStatement : Statement(), ArgumentHolder { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is ThrowStatement) return false + if (other !is ThrowExpression) return false return super.equals(other) && exception == other.exception && parentException == other.parentException diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt index 7e0759d654..49b5028ff0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt @@ -132,7 +132,7 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { is ForStatement -> handleForStatement(node) is SwitchStatement -> handleSwitchStatement(node) is IfStatement -> handleIfStatement(node) - is ThrowStatement -> handleThrowStatement(node) + is ThrowExpression -> handleThrowExpression(node) // Declarations is FieldDeclaration -> handleFieldDeclaration(node) is FunctionDeclaration -> handleFunctionDeclaration(node, functionSummaries) @@ -169,8 +169,8 @@ 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) { + /** Handle a [ThrowExpression]. The exception and parent exception flow into the node. */ + protected fun handleThrowExpression(node: ThrowExpression) { node.exception?.let { node.prevDFGEdges += it } node.parentException?.let { node.prevDFGEdges += it } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt index abac244c2d..c4b7fd231d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt @@ -193,7 +193,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa map[LookupScopeStatement::class.java] = { handleLookupScopeStatement(it as LookupScopeStatement) } - map[ThrowStatement::class.java] = { handleThrowStatement(it as ThrowStatement) } + map[ThrowExpression::class.java] = { handleThrowExpression(it as ThrowExpression) } } protected fun doNothing() { @@ -1274,9 +1274,9 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa /** * Calls [handleThrowOperator]. * - * See [Specification](https://fraunhofer-aisec.github.io/cpg/CPG/specs/eog/#throwstatement) + * See [Specification](https://fraunhofer-aisec.github.io/cpg/CPG/specs/eog/#throwexpression) */ - protected fun handleThrowStatement(statement: ThrowStatement) { + protected fun handleThrowExpression(statement: ThrowExpression) { handleThrowOperator( statement, statement.exception?.type, diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowStatementTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowExpressionTest.kt similarity index 94% rename from cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowStatementTest.kt rename to cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowExpressionTest.kt index bb459a2984..bf305a9157 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowStatementTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowExpressionTest.kt @@ -29,13 +29,13 @@ import de.fraunhofer.aisec.cpg.GraphExamples.Companion.testFrontend import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.graph.builder.* -import de.fraunhofer.aisec.cpg.graph.statements.ThrowStatement +import de.fraunhofer.aisec.cpg.graph.statements.ThrowExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.test.assertLocalName import kotlin.test.* -class ThrowStatementTest { +class ThrowExpressionTest { @Test fun testThrow() { val result = @@ -69,13 +69,13 @@ class ThrowStatementTest { assertIs(body) val emptyThrow = body.statements.getOrNull(0) - assertIs(emptyThrow) + assertIs(emptyThrow) println(emptyThrow.toString()) // This is only here to simulate a higher test coverage assertNull(emptyThrow.exception) assertTrue(emptyThrow.prevDFG.isEmpty()) val throwWithExc = body.statements.getOrNull(1) - assertIs(throwWithExc) + assertIs(throwWithExc) println(throwWithExc.toString()) // This is only here to simulate a higher test coverage val throwCall = throwWithExc.exception assertIs(throwCall) @@ -83,7 +83,7 @@ class ThrowStatementTest { assertEquals(setOf(throwCall), throwWithExc.prevDFG.toSet()) val throwWithExcAndParent = body.statements.getOrNull(2) - assertIs(throwWithExcAndParent) + assertIs(throwWithExcAndParent) println( throwWithExcAndParent.toString() ) // This is only here to simulate a higher test coverage diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/DataflowTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/DataflowTest.kt index 06b4c8ba70..6c2008fff0 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/DataflowTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/DataflowTest.kt @@ -28,7 +28,7 @@ package de.fraunhofer.aisec.cpg.graph.edges.flows import de.fraunhofer.aisec.cpg.GraphExamples.Companion.prepareThrowDFGTest import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.statements.ThrowStatement +import de.fraunhofer.aisec.cpg.graph.statements.ThrowExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import kotlin.collections.firstOrNull @@ -118,7 +118,7 @@ class DataflowTest { assertIs(body) val throwStmt = body.statements.getOrNull(1) - assertIs(throwStmt) + assertIs(throwStmt) assertNotNull(throwStmt.exception) val throwCall = throwStmt.exception assertIs(throwCall) diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt index 1647567d08..4f2a7bd0a4 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt @@ -400,7 +400,8 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : // need to information about the parenthesis. return input as Expression } - IASTUnaryExpression.op_throw -> operatorCode = "throw" + IASTUnaryExpression.op_throw -> + return newThrowExpression(rawNode = ctx).apply { this.exception = input } IASTUnaryExpression.op_typeid -> operatorCode = "typeid" IASTUnaryExpression.op_alignOf -> operatorCode = "alignof" IASTUnaryExpression.op_sizeofParameterPack -> operatorCode = "sizeof..." diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt index df320075fe..d8cad4dcb6 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt @@ -79,9 +79,8 @@ class StatementHandler(lang: JavaLanguageFrontend?) : stmt: Statement ): de.fraunhofer.aisec.cpg.graph.statements.Statement { val throwStmt = stmt as ThrowStmt - val throwOperation = - this.newUnaryOperator("throw", postfix = false, prefix = true, rawNode = stmt) - throwOperation.input = + val throwOperation = newThrowExpression(rawNode = stmt) + throwOperation.exception = frontend.expressionHandler.handle(throwStmt.expression) as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression return throwOperation diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt index 6376fc98ab..3c7763836c 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt @@ -81,7 +81,6 @@ class ExpressionHandler(lang: LLVMIRLanguageFrontend) : } LLVMMetadataAsValueValueKind, LLVMInlineAsmValueKind -> { - // TODO return newProblemExpression( "Metadata or ASM value kind not supported yet", ProblemNode.ProblemType.TRANSLATION, diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt index 4f74abc63c..c1e84c625b 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt @@ -40,7 +40,7 @@ class LLVMIRLanguage : Language() { override val frontend: KClass = LLVMIRLanguageFrontend::class override val compoundAssignmentOperators = setOf() - // TODO: In theory, the integers can have any bitwidth from 1 to 1^32 bits. It's not known if + // TODO: In theory, the integers can have any bit-width from 1 to 1^32 bits. It's not known if // they are interpreted as signed or unsigned. @Transient override val builtInTypes = diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt index 1c2687fa68..f9d8e8c7a1 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt @@ -28,7 +28,6 @@ package de.fraunhofer.aisec.cpg.frontends.llvm import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.* @@ -175,7 +174,10 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : LLVMResume -> { // Resumes propagation of an existing (in-flight) exception whose unwinding was // interrupted with a landingpad instruction. - return newUnaryOperator("throw", postfix = false, prefix = true, rawNode = instr) + return newThrowExpression(rawNode = instr).apply { + exception = + newProblemExpression("We don't know the exception while parsing this node.") + } } LLVMLandingPad -> { return handleLandingpad(instr) @@ -335,10 +337,13 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : } else { // "unwind to caller". As we don't know where the control flow continues, // the best model would be to throw the exception again. Here, we only know - // that we will throw something here but we don't know what. We have to fix + // that we will throw something here, but we don't know what. We have to fix // that later once we know in which catch-block this statement is executed. val throwOperation = - newUnaryOperator("throw", postfix = false, prefix = true, rawNode = instr) + newThrowExpression(rawNode = instr).apply { + exception = + newProblemExpression("We don't know the exception while parsing this node.") + } currentIfStatement?.elseStatement = throwOperation } @@ -1390,11 +1395,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : // We have multiple pairs, so we insert a declaration at the beginning of the function and // make an assignment in each BB. val functionName = LLVMGetValueName(bbsFunction).string - val functions = - tu.declarations.filter { d -> - (d as? FunctionDeclaration)?.name != null && - (d as? FunctionDeclaration)?.name.toString() == functionName - } + val functions = tu.functions(functionName) if (functions.size != 1) { log.error( "${functions.size} functions match the name of the one where the phi instruction is inserted. Can't handle this case." @@ -1402,7 +1403,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : throw TranslationException("Wrong number of functions for phi statement.") } // Create the dummy declaration at the beginning of the function body - val firstBB = (functions[0] as FunctionDeclaration).body as Block + val firstBB = functions[0].body as Block val varName = instr.name val type = frontend.typeOf(instr) val declaration = newVariableDeclaration(varName, type, false, rawNode = instr) diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt index 082de02842..12247af000 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt @@ -31,7 +31,6 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.passes.configuration.ExecuteFirst @@ -162,7 +161,7 @@ class CompressLLVMPass(ctx: TranslationContext) : ComponentPass(ctx) { } node.catchClauses = catchClauses - fixThrowStatementsForCatch(node.catchClauses[0]) + fixThrowExpressionsForCatch(node.catchClauses[0]) } node.catchClauses.size == 1 && node.catchClauses[0].body?.statements?.get(0) is Block -> { @@ -172,27 +171,25 @@ class CompressLLVMPass(ctx: TranslationContext) : ComponentPass(ctx) { // the compound statement the body of the catch clause. val innerCompound = node.catchClauses[0].body?.statements?.get(0) as? Block innerCompound?.statements?.let { node.catchClauses[0].body?.statements = it } - fixThrowStatementsForCatch(node.catchClauses[0]) + fixThrowExpressionsForCatch(node.catchClauses[0]) } node.catchClauses.isNotEmpty() -> { for (catch in node.catchClauses) { - fixThrowStatementsForCatch(catch) + fixThrowExpressionsForCatch(catch) } } } } /** - * Checks if a throw statement which is included in this catch block does not have a parameter. - * Those statements have been artificially added e.g. by a catchswitch and need to be filled + * Checks if a throw expression which is included in this catch block does not have a parameter. + * Those expressions have been artificially added e.g. by a catchswitch and need to be filled * now. */ - private fun fixThrowStatementsForCatch(catch: CatchClause) { + private fun fixThrowExpressionsForCatch(catch: CatchClause) { val reachableThrowNodes = - getAllChildrenRecursively(catch).filter { n -> - n is UnaryOperator && - n.operatorCode?.equals("throw") == true && - n.input is ProblemExpression + getAllChildrenRecursively(catch).filterIsInstance().filter { n -> + n.exception is ProblemExpression } if (reachableThrowNodes.isNotEmpty()) { if (catch.parameter == null) { @@ -212,7 +209,7 @@ class CompressLLVMPass(ctx: TranslationContext) : ComponentPass(ctx) { ) exceptionReference.language = catch.language exceptionReference.refersTo = catch.parameter - reachableThrowNodes.forEach { n -> (n as UnaryOperator).input = exceptionReference } + reachableThrowNodes.forEach { n -> n.exception = exceptionReference } } } diff --git a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt index 73a30f3333..0f6e0567d7 100644 --- a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt +++ b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt @@ -920,34 +920,29 @@ class LLVMIRLanguageFrontendTest { val tryStatement = (funcF.bodyOrNull(0)?.subStatement as? Block) ?.statements - ?.firstOrNull { s -> s is TryStatement } as? TryStatement - assertNotNull(tryStatement) + ?.firstOrNull { s -> s is TryStatement } + assertIs(tryStatement) assertEquals(2, tryStatement.tryBlock?.statements?.size) assertFullName( "_CxxThrowException", tryStatement.tryBlock?.statements?.get(0) as? CallExpression ) - assertEquals( + assertLocalName( "end", - (tryStatement.tryBlock?.statements?.get(1) as? GotoStatement) - ?.targetLabel - ?.name - ?.localName + (tryStatement.tryBlock?.statements?.get(1) as? GotoStatement)?.targetLabel ) assertEquals(1, tryStatement.catchClauses.size) - val catchSwitchExpr = - tryStatement.catchClauses[0].body?.statements?.get(0) as? DeclarationStatement - assertNotNull(catchSwitchExpr) + val catchSwitchExpr = tryStatement.catchClauses[0].body?.statements?.get(0) + assertIs(catchSwitchExpr) val catchswitchCall = (catchSwitchExpr.singleDeclaration as? VariableDeclaration)?.initializer - as? CallExpression - assertNotNull(catchswitchCall) + assertIs(catchswitchCall) assertFullName("llvm.catchswitch", catchswitchCall) - val ifExceptionMatches = - tryStatement.catchClauses[0].body?.statements?.get(1) as? IfStatement - val matchesExceptionCall = ifExceptionMatches?.condition as? CallExpression - assertNotNull(matchesExceptionCall) + val ifExceptionMatches = tryStatement.catchClauses[0].body?.statements?.get(1) + assertIs(ifExceptionMatches) + val matchesExceptionCall = ifExceptionMatches.condition + assertIs(matchesExceptionCall) assertFullName("llvm.matchesCatchpad", matchesExceptionCall) assertEquals( catchSwitchExpr.singleDeclaration, @@ -957,8 +952,8 @@ class LLVMIRLanguageFrontendTest { assertEquals(64L, (matchesExceptionCall.arguments[2] as Literal<*>).value as Long) assertEquals(null, (matchesExceptionCall.arguments[3] as Literal<*>).value) - val catchBlock = ifExceptionMatches.thenStatement as? Block - assertNotNull(catchBlock) + val catchBlock = ifExceptionMatches.thenStatement + assertIs(catchBlock) assertFullName( "llvm.catchpad", ((catchBlock.statements[0] as? DeclarationStatement)?.singleDeclaration @@ -966,8 +961,8 @@ class LLVMIRLanguageFrontendTest { ?.initializer as? CallExpression ) - val innerTry = catchBlock.statements[1] as? TryStatement - assertNotNull(innerTry) + val innerTry = catchBlock.statements[1] + assertIs(innerTry) assertFullName( "_CxxThrowException", innerTry.tryBlock?.statements?.get(0) as? CallExpression @@ -979,25 +974,20 @@ class LLVMIRLanguageFrontendTest { val innerCatchClause = (innerTry.catchClauses[0].body?.statements?.get(1) as? IfStatement)?.thenStatement - as? Block - assertNotNull(innerCatchClause) + assertIs(innerCatchClause) assertFullName( "llvm.catchpad", ((innerCatchClause.statements[0] as? DeclarationStatement)?.singleDeclaration as? VariableDeclaration) - ?.initializer as? CallExpression + ?.initializer ) assertLocalName("try.cont", (innerCatchClause.statements[1] as? GotoStatement)?.targetLabel) val innerCatchThrows = (innerTry.catchClauses[0].body?.statements?.get(1) as? IfStatement)?.elseStatement - as? UnaryOperator - assertNotNull(innerCatchThrows) - assertNotNull(innerCatchThrows.input) - assertSame( - innerTry.catchClauses[0].parameter, - (innerCatchThrows.input as? Reference)?.refersTo - ) + assertIs(innerCatchThrows) + assertNotNull(innerCatchThrows.exception) + assertRefersTo(innerCatchThrows.exception, innerTry.catchClauses[0].parameter) } // TODO: Write test for calling a vararg function (e.g. printf). LLVM code snippets can already diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt index bb75836162..a8ea976d29 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt @@ -88,10 +88,10 @@ class StatementHandler(frontend: PythonLanguageFrontend) : /** * Translates a Python [`Raise`](https://docs.python.org/3/library/ast.html#ast.Raise) into a - * [ThrowStatement]. + * [ThrowExpression]. */ - private fun handleRaise(node: Python.AST.Raise): ThrowStatement { - val ret = newThrowStatement(rawNode = node) + private fun handleRaise(node: Python.AST.Raise): ThrowExpression { + val ret = newThrowExpression(rawNode = node) node.exc?.let { ret.exception = frontend.expressionHandler.handle(it) } node.cause?.let { ret.parentException = frontend.expressionHandler.handle(it) } return ret @@ -204,7 +204,7 @@ class StatementHandler(frontend: PythonLanguageFrontend) : exitCallWithSysExec.addArgument(starOp) val ifStmt = newIfStatement().implicit() - ifStmt.thenStatement = newThrowStatement().implicit() + ifStmt.thenStatement = newThrowExpression().implicit() val neg = newUnaryOperator("not", false, false).implicit() neg.input = exitCallWithSysExec ifStmt.condition = neg diff --git a/docs/docs/CPG/specs/dfg.md b/docs/docs/CPG/specs/dfg.md index 00b25e56fe..fa754e981a 100755 --- a/docs/docs/CPG/specs/dfg.md +++ b/docs/docs/CPG/specs/dfg.md @@ -429,7 +429,7 @@ The data flow from the input to this node and, in case of the operatorCodes ++ a *Dangerous: We have to ensure that the first operation is performed before the last one (if applicable)* -## ThrowStatement +## ThrowExpression Interesting fields: @@ -441,7 +441,7 @@ The return value flows to the whole statement. Scheme: ```mermaid flowchart LR - exception -- DFG --> node([ThrowStatement]); + exception -- DFG --> node([ThrowExpression]); parentException -- DFG --> node; exception -.- node; parentException -.- node; diff --git a/docs/docs/CPG/specs/eog.md b/docs/docs/CPG/specs/eog.md index b23e17e404..4dc34fa6e5 100644 --- a/docs/docs/CPG/specs/eog.md +++ b/docs/docs/CPG/specs/eog.md @@ -379,7 +379,7 @@ flowchart LR ``` -## ThrowStatement +## ThrowExpression The EOG continues at an exception catching structure or a function that does a re-throw. Interesting fields: @@ -394,7 +394,7 @@ flowchart LR prev:::outer --EOG--> child1["exception"] child1 --EOG--> child2["parentException"] child2 --EOG-->parent - parent(["ThrowStatement"]) --EOG--> catchingContext:::outer + parent(["ThrowExpression"]) --EOG--> catchingContext:::outer parent -.-> child1 parent -.-> child2