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 103a5ecc52..3c0bc5bb98 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 @@ -194,7 +194,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() { @@ -1124,7 +1124,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa } /** Calls [handleThrowOperator]. */ - 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 78ca2cb401..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,7 +79,7 @@ class StatementHandler(lang: JavaLanguageFrontend?) : stmt: Statement ): de.fraunhofer.aisec.cpg.graph.statements.Statement { val throwStmt = stmt as ThrowStmt - val throwOperation = newThrowStatement(rawNode = stmt) + val throwOperation = newThrowExpression(rawNode = stmt) throwOperation.exception = frontend.expressionHandler.handle(throwStmt.expression) as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression 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 616b090a3b..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 @@ -174,7 +174,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : LLVMResume -> { // Resumes propagation of an existing (in-flight) exception whose unwinding was // interrupted with a landingpad instruction. - return newThrowStatement(rawNode = instr).apply { + return newThrowExpression(rawNode = instr).apply { exception = newProblemExpression("We don't know the exception while parsing this node.") } @@ -340,7 +340,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : // 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 = - newThrowStatement(rawNode = instr).apply { + newThrowExpression(rawNode = instr).apply { exception = newProblemExpression("We don't know the exception while parsing this node.") } 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 2f892460e8..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 @@ -161,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 -> { @@ -171,24 +171,24 @@ 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).filterIsInstance().filter { n -> + getAllChildrenRecursively(catch).filterIsInstance().filter { n -> n.exception is ProblemExpression } if (reachableThrowNodes.isNotEmpty()) { 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 53765379f1..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 @@ -985,7 +985,7 @@ class LLVMIRLanguageFrontendTest { val innerCatchThrows = (innerTry.catchClauses[0].body?.statements?.get(1) as? IfStatement)?.elseStatement - assertIs(innerCatchThrows) + assertIs(innerCatchThrows) assertNotNull(innerCatchThrows.exception) assertRefersTo(innerCatchThrows.exception, innerTry.catchClauses[0].parameter) } 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 84f341767b..d9780da824 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