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 0c9eb26b4b..a5400e768e 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 @@ -200,50 +200,115 @@ class FulfilledAndFailedPaths(val fulfilled: List>, val failed: List< * but not mandatory**. If the list "failed" is empty, the data flow is mandatory. */ fun Node.followPrevFullDFGEdgesUntilHit(predicate: (Node) -> Boolean): FulfilledAndFailedPaths { - val fulfilledPaths = mutableListOf>() - val failedPaths = mutableListOf>() - val worklist = mutableListOf>() - worklist.add(listOf(this)) + return followXUntilHit( + x = { currentNode -> currentNode.prevFullDFG }, + collectFailedPaths = true, + findAllPossiblePaths = true, + predicate = predicate + ) +} - while (worklist.isNotEmpty()) { - val currentPath = worklist.removeFirst() - if (currentPath.last().prevFullDFG.isEmpty()) { - // No further nodes in the path and the path criteria are not satisfied. - failedPaths.add(currentPath) - continue +fun Node.collectAllPrevCDGPaths(interproceduralAnalysis: Boolean): List> { + // We make everything fail to reach the end of the CDG. Then, we use the stuff collected in the + // failed paths (everything) + return this.followPrevCDGUntilHit( + collectFailedPaths = true, + findAllPossiblePaths = true, + interproceduralAnalysis = interproceduralAnalysis + ) { + false } + .failed +} - for (prev in currentPath.last().prevFullDFG) { - // Copy the path for each outgoing DFG edge and add the prev node - val nextPath = mutableListOf() - nextPath.addAll(currentPath) - nextPath.add(prev) - - if (predicate(prev)) { - fulfilledPaths.add(nextPath) - continue // Don't add this path anymore. The requirement is satisfied. - } - // The prev node is new in the current path (i.e., there's no loop), so we add the path - // with the next step to the worklist. - if (!currentPath.contains(prev)) { - worklist.add(nextPath) - } +fun Node.collectAllNextCDGPaths(interproceduralAnalysis: Boolean): List> { + // We make everything fail to reach the end of the CDG. Then, we use the stuff collected in the + // failed paths (everything) + return this.followNextCDGUntilHit( + collectFailedPaths = true, + findAllPossiblePaths = true, + interproceduralAnalysis = interproceduralAnalysis + ) { + false } - } - - return FulfilledAndFailedPaths(fulfilledPaths, failedPaths) + .failed } /** * Returns an instance of [FulfilledAndFailedPaths] where [FulfilledAndFailedPaths.fulfilled] - * contains all possible shortest data flow paths (with [FullDataflowGranularity]) between the - * starting node [this] and the end node fulfilling [predicate]. The paths are represented as lists - * of nodes. Paths which do not end at such a node are included in [FulfilledAndFailedPaths.failed]. + * contains all possible shortest data flow paths (with [ControlDependence]) between the starting + * node [this] and the end node fulfilling [predicate]. The paths are represented as lists of nodes. + * Paths which do not end at such a node are included in [FulfilledAndFailedPaths.failed]. * * Hence, if "fulfilled" is a non-empty list, a data flow from [this] to such a node is **possible * but not mandatory**. If the list "failed" is empty, the data flow is mandatory. */ -fun Node.followNextFullDFGEdgesUntilHit( +fun Node.followNextCDGUntilHit( + collectFailedPaths: Boolean = true, + findAllPossiblePaths: Boolean = true, + interproceduralAnalysis: Boolean = false, + predicate: (Node) -> Boolean +): FulfilledAndFailedPaths { + return followXUntilHit( + x = { currentNode -> + val nextNodes = currentNode.nextCDG.toMutableList() + if (interproceduralAnalysis) { + nextNodes.addAll((currentNode as? CallExpression)?.calls ?: listOf()) + } + nextNodes + }, + collectFailedPaths = collectFailedPaths, + findAllPossiblePaths = findAllPossiblePaths, + predicate = predicate + ) +} + +/** + * Returns an instance of [FulfilledAndFailedPaths] where [FulfilledAndFailedPaths.fulfilled] + * contains all possible shortest data flow paths (with [ControlDependence]) between the starting + * node [this] and the end node fulfilling [predicate] (backwards analysis). The paths are + * represented as lists of nodes. Paths which do not end at such a node are included in + * [FulfilledAndFailedPaths.failed]. + * + * Hence, if "fulfilled" is a non-empty list, a CDG path from [this] to such a node is **possible + * but not mandatory**. If the list "failed" is empty, the data flow is mandatory. + */ +fun Node.followPrevCDGUntilHit( + collectFailedPaths: Boolean = true, + findAllPossiblePaths: Boolean = true, + interproceduralAnalysis: Boolean = false, + predicate: (Node) -> Boolean +): FulfilledAndFailedPaths { + return followXUntilHit( + x = { currentNode -> + val nextNodes = currentNode.prevCDG.toMutableList() + if (interproceduralAnalysis) { + nextNodes.addAll( + (currentNode as? FunctionDeclaration)?.usages?.mapNotNull { + it.astParent as? CallExpression + } ?: listOf() + ) + } + nextNodes + }, + collectFailedPaths = collectFailedPaths, + findAllPossiblePaths = findAllPossiblePaths, + predicate = predicate + ) +} + +/** + * Returns an instance of [FulfilledAndFailedPaths] where [FulfilledAndFailedPaths.fulfilled] + * contains all possible shortest data flow paths (with [x] specifying how to fetch more nodes) + * between the starting node [this] and the end node fulfilling [predicate] (backwards analysis). + * The paths are represented as lists of nodes. Paths which do not end at such a node are included + * in [FulfilledAndFailedPaths.failed]. + * + * Hence, if "fulfilled" is a non-empty list, a path from [this] to such a node is **possible but + * not mandatory**. If the list "failed" is empty, the path is mandatory. + */ +fun Node.followXUntilHit( + x: (Node) -> List, collectFailedPaths: Boolean = true, findAllPossiblePaths: Boolean = true, predicate: (Node) -> Boolean @@ -264,15 +329,15 @@ fun Node.followNextFullDFGEdgesUntilHit( worklist.remove(currentPath) val currentNode = currentPath.last() alreadySeenNodes.add(currentNode) - // The last node of the path is where we continue. We get all of its outgoing DFG edges and + // The last node of the path is where we continue. We get all of its outgoing CDG edges and // follow them - if (currentNode.nextFullDFG.isEmpty()) { - // No further nodes in the path and the path criteria are not satisfied. - if (collectFailedPaths) failedPaths.add(currentPath) - } + var nextNodes = x(currentNode) + + // No further nodes in the path and the path criteria are not satisfied. + if (nextNodes.isEmpty() && collectFailedPaths) failedPaths.add(currentPath) - for (next in currentNode.nextFullDFG) { - // Copy the path for each outgoing DFG edge and add the next node + for (next in nextNodes) { + // Copy the path for each outgoing CDG edge and add the next node val nextPath = currentPath.toMutableList() nextPath.add(next) if (predicate(next)) { @@ -296,6 +361,28 @@ fun Node.followNextFullDFGEdgesUntilHit( return FulfilledAndFailedPaths(fulfilledPaths, failedPaths) } +/** + * Returns an instance of [FulfilledAndFailedPaths] where [FulfilledAndFailedPaths.fulfilled] + * contains all possible shortest data flow paths (with [FullDataflowGranularity]) between the + * starting node [this] and the end node fulfilling [predicate]. The paths are represented as lists + * of nodes. Paths which do not end at such a node are included in [FulfilledAndFailedPaths.failed]. + * + * Hence, if "fulfilled" is a non-empty list, a data flow from [this] to such a node is **possible + * but not mandatory**. If the list "failed" is empty, the data flow is mandatory. + */ +fun Node.followNextFullDFGEdgesUntilHit( + collectFailedPaths: Boolean = true, + findAllPossiblePaths: Boolean = true, + predicate: (Node) -> Boolean +): FulfilledAndFailedPaths { + return followXUntilHit( + x = { currentNode -> currentNode.nextFullDFG }, + collectFailedPaths = collectFailedPaths, + findAllPossiblePaths = findAllPossiblePaths, + predicate = predicate + ) +} + /** * Returns an instance of [FulfilledAndFailedPaths] where [FulfilledAndFailedPaths.fulfilled] * contains all possible shortest evaluation paths between the starting node [this] and the end node @@ -307,45 +394,14 @@ fun Node.followNextFullDFGEdgesUntilHit( * such a statement is always executed. */ fun Node.followNextEOGEdgesUntilHit(predicate: (Node) -> Boolean): FulfilledAndFailedPaths { - // Looks complicated but at least it's not recursive... - // result: List of paths (between from and to) - val fulfilledPaths = mutableListOf>() - // failedPaths: All the paths which do not satisfy "predicate" - val failedPaths = mutableListOf>() - // The list of paths where we're not done yet. - val worklist = mutableListOf>() - worklist.add(listOf(this)) // We start only with the "from" node (=this) - - while (worklist.isNotEmpty()) { - val currentPath = worklist.removeFirst() - // The last node of the path is where we continue. We get all of its outgoing DFG edges and - // follow them - if (currentPath.last().nextEOGEdges.none { it.unreachable != true }) { - // No further nodes in the path and the path criteria are not satisfied. - failedPaths.add(currentPath) - continue // Don't add this path anymore. The requirement is satisfied. - } - - for (next in - currentPath.last().nextEOGEdges.filter { it.unreachable != true }.map { it.end }) { - // Copy the path for each outgoing DFG edge and add the next node - val nextPath = mutableListOf() - nextPath.addAll(currentPath) - nextPath.add(next) - if (predicate(next)) { - // We ended up in the node "to", so we're done. Add the path to the results. - fulfilledPaths.add(nextPath) - continue // Don't add this path anymore. The requirement is satisfied. - } - // The next node is new in the current path (i.e., there's no loop), so we add the path - // with the next step to the worklist. - if (!currentPath.contains(next)) { - worklist.add(nextPath) - } - } - } - - return FulfilledAndFailedPaths(fulfilledPaths, failedPaths) + return followXUntilHit( + x = { currentNode -> + currentNode.nextEOGEdges.filter { it.unreachable != true }.map { it.end } + }, + collectFailedPaths = true, + findAllPossiblePaths = true, + predicate = predicate + ) } /** @@ -359,45 +415,14 @@ fun Node.followNextEOGEdgesUntilHit(predicate: (Node) -> Boolean): FulfilledAndF * such a statement is always executed. */ fun Node.followPrevEOGEdgesUntilHit(predicate: (Node) -> Boolean): FulfilledAndFailedPaths { - // Looks complicated but at least it's not recursive... - // result: List of paths (between from and to) - val fulfilledPaths = mutableListOf>() - // failedPaths: All the paths which do not satisfy "predicate" - val failedPaths = mutableListOf>() - // The list of paths where we're not done yet. - val worklist = mutableListOf>() - worklist.add(listOf(this)) // We start only with the "from" node (=this) - - while (worklist.isNotEmpty()) { - val currentPath = worklist.removeFirst() - // The last node of the path is where we continue. We get all of its outgoing DFG edges and - // follow them - if (currentPath.last().prevEOGEdges.none { it.unreachable != true }) { - // No further nodes in the path and the path criteria are not satisfied. - failedPaths.add(currentPath) - continue // Don't add this path anymore. The requirement is satisfied. - } - - for (next in - currentPath.last().prevEOGEdges.filter { it.unreachable != true }.map { it.start }) { - // Copy the path for each outgoing DFG edge and add the next node - val nextPath = mutableListOf() - nextPath.addAll(currentPath) - nextPath.add(next) - if (predicate(next)) { - // We ended up in the node "to", so we're done. Add the path to the results. - fulfilledPaths.add(nextPath) - continue // Don't add this path anymore. The requirement is satisfied. - } - // The next node is new in the current path (i.e., there's no loop), so we add the path - // with the next step to the worklist. - if (!currentPath.contains(next)) { - worklist.add(nextPath) - } - } - } - - return FulfilledAndFailedPaths(fulfilledPaths, failedPaths) + return followXUntilHit( + x = { currentNode -> + currentNode.prevEOGEdges.filter { it.unreachable != true }.map { it.start } + }, + collectFailedPaths = true, + findAllPossiblePaths = true, + predicate = predicate + ) } /** @@ -634,7 +659,7 @@ fun Node.firstParentOrNull(predicate: (Node) -> Boolean): Node? { return node } - // go up-wards in the ast tree + // go upwards in the ast tree node = node.astParent } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt index 6e323eb7c5..ac060fa20a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt @@ -32,7 +32,6 @@ import de.fraunhofer.aisec.cpg.graph.NodeBuilder.LOGGER import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log import de.fraunhofer.aisec.cpg.graph.scopes.Scope import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.helpers.getCodeOfSubregion import de.fraunhofer.aisec.cpg.passes.inference.IsImplicitProvider import de.fraunhofer.aisec.cpg.passes.inference.IsInferredProvider @@ -299,10 +298,16 @@ fun T.codeAndLocationFromOtherRawNode(rawNode: AstNode?): T * code is extracted from the parent node to catch separators and auxiliary syntactic elements that * are between the child nodes. * - * @param parentNode Used to extract the code for this node + * @param parentNode Used to extract the code for this node. + * @param newLineType The char(s) used to describe a new line, usually either "\n" or "\r\n". This + * is needed because the location block spanning the children usually comprises more than one + * line. */ context(CodeAndLocationProvider) -fun T.codeAndLocationFromChildren(parentNode: AstNode): T { +fun T.codeAndLocationFromChildren( + parentNode: AstNode, + lineBreakSequence: CharSequence = "\n" +): T { var first: Node? = null var last: Node? = null @@ -358,7 +363,7 @@ fun T.codeAndLocationFromChildren(parentNode: AstNode): T { val parentRegion = this@CodeAndLocationProvider.locationOf(parentNode)?.region if (parentCode != null && parentRegion != null) { // If the parent has code and region the new region is used to extract the code - this.code = getCodeOfSubregion(parentCode, parentRegion, newRegion) + this.code = getCodeOfSubregion(parentCode, parentRegion, newRegion, lineBreakSequence) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Dataflow.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Dataflow.kt index 206e554d31..900eedc09b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Dataflow.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Dataflow.kt @@ -33,8 +33,10 @@ import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeSet import de.fraunhofer.aisec.cpg.graph.edges.collections.MirroredEdgeCollection import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.HasType +import de.fraunhofer.aisec.cpg.helpers.neo4j.DataflowGranularityConverter import kotlin.reflect.KProperty import org.neo4j.ogm.annotation.* +import org.neo4j.ogm.annotation.typeconversion.Convert /** * The granularity of the data-flow, e.g., whether the flow contains the whole object, or just a @@ -87,7 +89,9 @@ open class Dataflow( start: Node, end: Node, /** The granularity of this dataflow. */ - @Transient @JsonIgnore var granularity: Granularity = default() + @Convert(DataflowGranularityConverter::class) + @JsonIgnore + var granularity: Granularity = default() ) : Edge(start, end) { override val label: String = "DFG" diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/RegionUtils.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/RegionUtils.kt index e43c3d35c9..969225e69b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/RegionUtils.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/RegionUtils.kt @@ -25,50 +25,39 @@ */ package de.fraunhofer.aisec.cpg.helpers -import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.sarif.Region import kotlin.math.min import org.apache.commons.lang3.StringUtils /** - * To prevent issues with different newline types and formatting. - * - * @param multilineCode The newline type is extracted from the code assuming it contains newlines - * @return the String of the newline or \n as default + * Returns the part of the [code] described by [subRegion], embedded in [nodeRegion]. [newLineType] + * can be used to specify the type of new-line char(s) used on the platform. */ -fun getNewLineType(multilineCode: String, region: Region? = null): String { - var code = multilineCode - region?.let { - if (it.startLine != it.endLine) { - code = code.substring(0, code.length - it.endColumn + 1) - } - } - - val nls = listOf("\n\r", "\r\n", "\n") - for (nl in nls) { - if (code.endsWith(nl)) { - return nl - } - } - LanguageFrontend.log.debug("Could not determine newline type. Assuming \\n.") - return "\n" -} - -fun getCodeOfSubregion(code: String, nodeRegion: Region, subRegion: Region): String { - val nlType = getNewLineType(code, nodeRegion) +fun getCodeOfSubregion( + code: String, + nodeRegion: Region, + subRegion: Region, + lineBreakSequence: CharSequence = "\n" +): String { val start = if (subRegion.startLine == nodeRegion.startLine) { subRegion.startColumn - nodeRegion.startColumn } else { - (StringUtils.ordinalIndexOf(code, nlType, subRegion.startLine - nodeRegion.startLine) + - subRegion.startColumn) + (StringUtils.ordinalIndexOf( + code, + lineBreakSequence, + subRegion.startLine - nodeRegion.startLine + ) + subRegion.startColumn) } var end = if (subRegion.endLine == nodeRegion.startLine) { subRegion.endColumn - nodeRegion.startColumn } else { - (StringUtils.ordinalIndexOf(code, nlType, subRegion.endLine - nodeRegion.startLine) + - subRegion.endColumn) + (StringUtils.ordinalIndexOf( + code, + lineBreakSequence, + subRegion.endLine - nodeRegion.startLine + ) + subRegion.endColumn) } // Unfortunately, we sometimes have issues with (non)-Unicode characters in code, where the diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/neo4j/DataflowGranularityConverter.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/neo4j/DataflowGranularityConverter.kt new file mode 100644 index 0000000000..3db0495b7e --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/neo4j/DataflowGranularityConverter.kt @@ -0,0 +1,61 @@ +/* + * 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.helpers.neo4j + +import de.fraunhofer.aisec.cpg.graph.edges.flows.Granularity +import de.fraunhofer.aisec.cpg.graph.edges.flows.PartialDataflowGranularity + +/** This converter converts a [Granularity] into a string-based representation in Neo4J. */ +class DataflowGranularityConverter : CpgCompositeConverter { + companion object { + const val FIELD_GRANULARITY = "granularity" + const val FIELD_PARTIAL_TARGET = "partialTarget" + } + + override fun toGraphProperties(value: Granularity): MutableMap { + val map = mutableMapOf() + + val type = value::class.simpleName?.substringBefore("DataflowGranularity")?.uppercase() + if (type != null) { + // The type of granularity + map[FIELD_GRANULARITY] = type + } + + // Only for partial + if (value is PartialDataflowGranularity) { + map[FIELD_PARTIAL_TARGET] = value.partialTarget?.name.toString() + } + + return map + } + + override val graphSchema: List> + get() = listOf(Pair("String", FIELD_GRANULARITY), Pair("String", FIELD_PARTIAL_TARGET)) + + override fun toEntityAttribute(value: MutableMap): Granularity { + throw UnsupportedOperationException() + } +} 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 c4b7fd231d..096c67c11a 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 @@ -641,46 +641,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa /** See [Specification](https://fraunhofer-aisec.github.io/cpg/CPG/specs/eog/#unaryoperator) */ protected fun handleUnaryOperator(node: UnaryOperator) { - // TODO(oxisto): These operator codes are highly language specific and might be more suited - // to be handled differently (see https://github.com/Fraunhofer-AISEC/cpg/issues/1161) - if (node.operatorCode == "throw") { - handleThrowOperator(node, node.input.type, node.input) - } else { - handleUnspecificUnaryOperator(node) - } - } - - /** - * Generates the EOG for a [node] which represents a statement/expression which throws an - * exception. Since some languages may accept different inputs to a throw statement (typically - * 1, sometimes 2, 0 is also possible), we have collect these in [inputs]. The input which is - * evaluated first, must be the first item in the vararg! Any `null` object in `inputs` will be - * filtered. We connect the throw statement internally, i.e., the inputs are evaluated from - * index 0 to n and then the whole node is evaluated. - */ - protected fun handleThrowOperator(node: Node, throwType: Type?, vararg inputs: Expression?) { - inputs.filterNotNull().forEach { handleEOG(it) } - attachToEOG(node) - - if (throwType != null) { - // Here, we identify the encapsulating ast node that can handle or relay a throw - val handlingOrRelayingParent = - node.firstParentOrNull { parent -> - parent is TryStatement || parent is FunctionDeclaration - } - if (handlingOrRelayingParent != null) { - val throwByTypeMap = - nodesToInternalThrows.getOrPut(handlingOrRelayingParent) { mutableMapOf() } - val throwEOGExits = throwByTypeMap.getOrPut(throwType) { mutableListOf() } - throwEOGExits.addAll(currentPredecessors.toMutableList()) - } else { - LOGGER.error( - "Cannot attach throw to a parent node, throw is neither in a try statement nor in a relaying function." - ) - } - } - // After a throw, the eog is not progressing in the following ast subtrees - currentPredecessors.clear() + handleUnspecificUnaryOperator(node) } /** @@ -1276,15 +1237,52 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa * * See [Specification](https://fraunhofer-aisec.github.io/cpg/CPG/specs/eog/#throwexpression) */ - protected fun handleThrowExpression(statement: ThrowExpression) { + protected fun handleThrowExpression(throwExpression: ThrowExpression) { handleThrowOperator( - statement, - statement.exception?.type, - statement.exception, - statement.parentException + throwExpression, + throwExpression.exception?.type, + throwExpression.exception, + throwExpression.parentException ) } + /** + * Generates the EOG for a [throwExpression] which represents a statement/expression which + * throws an exception. Since some languages may accept different inputs to a throw statement + * (typically 1, sometimes 2, 0 is also possible), we have collect these in [inputs]. The input + * which is evaluated first, must be the first item in the vararg! Any `null` object in `inputs` + * will be filtered. We connect the throw statement internally, i.e., the inputs are evaluated + * from index 0 to n and then the whole node is evaluated. + */ + protected fun handleThrowOperator( + throwExpression: Node, + throwType: Type?, + vararg inputs: Expression? + ) { + inputs.filterNotNull().forEach { handleEOG(it) } + attachToEOG(throwExpression) + + if (throwType != null) { + // Here, we identify the encapsulating ast node that can handle or relay a throw + val handlingOrRelayingParent = + throwExpression.firstParentOrNull { parent -> + parent is TryStatement || parent is FunctionDeclaration + } + if (handlingOrRelayingParent != null) { + val throwByTypeMap = + nodesToInternalThrows.getOrPut(handlingOrRelayingParent) { mutableMapOf() } + val throwEOGExits = throwByTypeMap.getOrPut(throwType) { mutableListOf() } + throwEOGExits.addAll(currentPredecessors.toMutableList()) + } else { + LOGGER.error( + "Cannot attach throw to a parent node, throw is neither in a try statement nor in a relaying function." + ) + } + } + // After a throw, the eog is not progressing in the following ast subtrees + currentPredecessors.clear() + } + companion object { protected val LOGGER = LoggerFactory.getLogger(EvaluationOrderGraphPass::class.java) 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 0f6e0567d7..dbeeb0ad41 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 @@ -71,15 +71,21 @@ class LLVMIRLanguageFrontendTest { assertNotNull(main) assertLocalName("i32", main.type) - val xVector = - (main.bodyOrNull(0)?.statements?.get(0) as? DeclarationStatement) - ?.singleDeclaration as? VariableDeclaration - val xInit = xVector?.initializer as? InitializerListExpression - assertNotNull(xInit) - assertLocalName("poison", xInit.initializers[0] as? Reference) - assertEquals(0L, (xInit.initializers[1] as? Literal<*>)?.value) - assertEquals(0L, (xInit.initializers[2] as? Literal<*>)?.value) - assertEquals(0L, (xInit.initializers[3] as? Literal<*>)?.value) + // We want to see that the declaration is the very first statement of the method body (it's + // wrapped inside another block). + val mainBody = main.bodyOrNull(0) + assertIs(mainBody) + val declarationStmt = mainBody.statements.firstOrNull() + assertIs(declarationStmt) + val xVector = declarationStmt.singleDeclaration + assertIs(xVector) + val xInit = xVector.initializer + assertIs(xInit) + assertIs(xInit.initializers[0]) + assertLocalName("poison", xInit.initializers[0]) + assertLiteralValue(0L, xInit.initializers[1]) + assertLiteralValue(0L, xInit.initializers[2]) + assertLiteralValue(0L, xInit.initializers[3]) } @Test @@ -104,25 +110,25 @@ class LLVMIRLanguageFrontendTest { assertNotNull(rand) assertNull(rand.body) - val decl = tu.variables["x"] - assertNotNull(decl) + val xDeclaration = tu.variables["x"] + assertNotNull(xDeclaration) - val call = decl.initializer as? CallExpression - assertNotNull(call) + val call = xDeclaration.initializer + assertIs(call) assertLocalName("rand", call) - assertTrue(call.invokes.contains(rand)) + assertContains(call.invokes, rand) assertEquals(0, call.arguments.size) val xorStatement = main.bodyOrNull(3) assertNotNull(xorStatement) - val xorDecl = xorStatement.singleDeclaration as? VariableDeclaration - assertNotNull(xorDecl) - assertLocalName("a", xorDecl) - assertEquals("i32", xorDecl.type.typeName) + val xorDeclaration = xorStatement.singleDeclaration + assertIs(xorDeclaration) + assertLocalName("a", xorDeclaration) + assertEquals("i32", xorDeclaration.type.typeName) - val xor = xorDecl.initializer as? BinaryOperator - assertNotNull(xor) + val xor = xorDeclaration.initializer + assertIs(xor) assertEquals("^", xor.operatorCode) } @@ -163,58 +169,57 @@ class LLVMIRLanguageFrontendTest { val s = foo.parameters.firstOrNull { it.name.localName == "s" } assertNotNull(s) - val arrayidx = - foo.bodyOrNull(0)?.singleDeclaration as? VariableDeclaration + val arrayidx = foo.variables["arrayidx"] assertNotNull(arrayidx) // arrayidx will be assigned to a chain of the following expressions: // &s[1].field2.field1[5][13] // we will check them in the reverse order (after the unary operator) - val unary = arrayidx.initializer as? UnaryOperator - assertNotNull(unary) + val unary = arrayidx.initializer + assertIs(unary) assertEquals("&", unary.operatorCode) - var arrayExpr = unary.input as? SubscriptExpression - assertNotNull(arrayExpr) + var arrayExpr = unary.input + assertIs(arrayExpr) assertLocalName("13", arrayExpr) - assertEquals( + assertLiteralValue( 13L, - (arrayExpr.subscriptExpression as? Literal<*>)?.value + arrayExpr.subscriptExpression ) // should this be integer instead of long? - arrayExpr = arrayExpr.arrayExpression as? SubscriptExpression - assertNotNull(arrayExpr) + arrayExpr = arrayExpr.arrayExpression + assertIs(arrayExpr) assertLocalName("5", arrayExpr) - assertEquals( + assertLiteralValue( 5L, - (arrayExpr.subscriptExpression as? Literal<*>)?.value + arrayExpr.subscriptExpression ) // should this be integer instead of long? - var memberExpression = arrayExpr.arrayExpression as? MemberExpression - assertNotNull(memberExpression) + var memberExpression = arrayExpr.arrayExpression + assertIs(memberExpression) assertLocalName("field_1", memberExpression) - memberExpression = memberExpression.base as? MemberExpression - assertNotNull(memberExpression) + memberExpression = memberExpression.base + assertIs(memberExpression) assertLocalName("field_2", memberExpression) - arrayExpr = memberExpression.base as? SubscriptExpression - assertNotNull(arrayExpr) + arrayExpr = memberExpression.base + assertIs(arrayExpr) assertLocalName("1", arrayExpr) - assertEquals( + assertLiteralValue( 1L, - (arrayExpr.subscriptExpression as? Literal<*>)?.value + arrayExpr.subscriptExpression ) // should this be integer instead of long? - val ref = arrayExpr.arrayExpression as? Reference - assertNotNull(ref) + val ref = arrayExpr.arrayExpression + assertIs(ref) assertLocalName("s", ref) - assertSame(s, ref.refersTo) + assertRefersTo(ref, s) } @Test - fun testSwitchCase() { // TODO: Update the test + fun testSwitchCase() { val topLevel = Path.of("src", "test", "resources", "llvm") val tu = analyzeAndGetFirstTU( @@ -231,17 +236,17 @@ class LLVMIRLanguageFrontendTest { val onzeroLabel = main.labels.getOrNull(0) assertNotNull(onzeroLabel) assertLocalName("onzero", onzeroLabel) - assertTrue(onzeroLabel.subStatement is Block) + assertIs(onzeroLabel.subStatement) val ononeLabel = main.labels.getOrNull(1) assertNotNull(ononeLabel) assertLocalName("onone", ononeLabel) - assertTrue(ononeLabel.subStatement is Block) + assertIs(ononeLabel.subStatement) val defaultLabel = main.labels.getOrNull(2) assertNotNull(defaultLabel) assertLocalName("otherwise", defaultLabel) - assertTrue(defaultLabel.subStatement is Block) + assertIs(defaultLabel.subStatement) // Check that the type of %a is i32 val a = main.variables["a"] @@ -254,21 +259,24 @@ class LLVMIRLanguageFrontendTest { assertNotNull(switchStatement) // Check that we have switch(a) - assertSame(a, (switchStatement.selector as Reference).refersTo) + assertRefersTo(switchStatement.selector, a) - val cases = switchStatement.statement as Block + val cases = switchStatement.statement + assertIs(cases) // Check that the first case is case 0 -> goto onzero and that the BB is inlined - val case1 = cases.statements[0] as CaseStatement - assertEquals(0L, (case1.caseExpression as Literal<*>).value as Long) + val case1 = cases.statements[0] + assertIs(case1) + assertLiteralValue(0L, case1.caseExpression) assertSame(onzeroLabel.subStatement, cases.statements[1]) // Check that the second case is case 1 -> goto onone and that the BB is inlined - val case2 = cases.statements[2] as CaseStatement - assertEquals(1L, (case2.caseExpression as Literal<*>).value as Long) + val case2 = cases.statements[2] + assertIs(case2) + assertLiteralValue(1L, case2.caseExpression) assertSame(ononeLabel.subStatement, cases.statements[3]) // Check that the default location is inlined val defaultStatement = cases.statements[4] as? DefaultStatement - assertNotNull(defaultStatement) + assertIs(defaultStatement) assertSame(defaultLabel.subStatement, cases.statements[5]) } @@ -288,60 +296,75 @@ class LLVMIRLanguageFrontendTest { // Test that the types and values of the comparison expression are correct val icmpStatement = main.bodyOrNull(1) assertNotNull(icmpStatement) - val variableDecl = icmpStatement.declarations[0] as VariableDeclaration - val comparison = variableDecl.initializer as BinaryOperator + val variableDecl = icmpStatement.declarations[0] + assertIs(variableDecl) + val comparison = variableDecl.initializer + assertIs(comparison) assertEquals("==", comparison.operatorCode) - val rhs = (comparison.rhs as Literal<*>) - val lhs = (comparison.lhs as Reference).refersTo as VariableDeclaration - assertEquals(10L, (rhs.value as Long)) + val rhs = comparison.rhs + assertIs>(rhs) + assertLiteralValue(10L, rhs) assertEquals(tu.primitiveType("i32"), rhs.type) - assertLocalName("x", comparison.lhs as Reference) - assertLocalName("x", lhs) - assertEquals(tu.primitiveType("i32"), lhs.type) + val lhsRef = comparison.lhs + assertIs(lhsRef) + assertLocalName("x", lhsRef) + val lhsDeclaration = lhsRef.refersTo + assertIs(lhsDeclaration) + assertLocalName("x", lhsDeclaration) + assertSame(tu.primitiveType("i32"), lhsDeclaration.type) // Check that the jump targets are set correctly val ifStatement = main.ifs.firstOrNull() assertNotNull(ifStatement) - assertEquals("IfUnequal", (ifStatement.elseStatement!! as GotoStatement).labelName) - val ifBranch = (ifStatement.thenStatement as Block) + val elseStatement = ifStatement.elseStatement + assertIs(elseStatement) + assertEquals("IfUnequal", elseStatement.labelName) + val thenBranch = ifStatement.thenStatement + assertIs(thenBranch) // Check that the condition is set correctly val ifCondition = ifStatement.condition - assertSame(variableDecl, (ifCondition as Reference).refersTo) + assertRefersTo(ifCondition, variableDecl) - val elseBranch = - (ifStatement.elseStatement!! as GotoStatement).targetLabel?.subStatement as Block + val elseBranch = elseStatement.targetLabel?.subStatement + assertIs(elseBranch) assertEquals(2, elseBranch.statements.size) assertEquals(" %y = mul i32 %x, 32768", elseBranch.statements[0].code) assertEquals(" ret i32 %y", elseBranch.statements[1].code) // Check that it's the correct then-branch - assertEquals(2, ifBranch.statements.size) - assertEquals(" %condUnsigned = icmp ugt i32 %x, -3", ifBranch.statements[0].code) - - val ifBranchVariableDecl = - (ifBranch.statements[0] as DeclarationStatement).declarations[0] as VariableDeclaration - val ifBranchComp = ifBranchVariableDecl.initializer as BinaryOperator + assertEquals(2, thenBranch.statements.size) + assertEquals(" %condUnsigned = icmp ugt i32 %x, -3", thenBranch.statements[0].code) + + val ifBranchDeclarationStatement = thenBranch.statements[0] + assertIs(ifBranchDeclarationStatement) + val ifBranchVariableDeclaration = ifBranchDeclarationStatement.declarations[0] + assertIs(ifBranchVariableDeclaration) + val ifBranchComp = ifBranchVariableDeclaration.initializer + assertIs(ifBranchComp) assertEquals(">", ifBranchComp.operatorCode) - assertTrue(ifBranchComp.rhs is CastExpression) - assertTrue(ifBranchComp.lhs is CastExpression) + assertIs(ifBranchComp.rhs) + assertIs(ifBranchComp.lhs) - val ifBranchCompRhs = ifBranchComp.rhs as CastExpression + val ifBranchCompRhs = ifBranchComp.rhs + assertIs(ifBranchCompRhs) assertEquals(tu.objectType("ui32"), ifBranchCompRhs.castType) assertEquals(tu.objectType("ui32"), ifBranchCompRhs.type) - val ifBranchCompLhs = ifBranchComp.lhs as CastExpression + val ifBranchCompLhs = ifBranchComp.lhs + assertIs(ifBranchCompLhs) assertEquals(tu.objectType("ui32"), ifBranchCompLhs.castType) assertEquals(tu.objectType("ui32"), ifBranchCompLhs.type) - val declRefExpr = ifBranchCompLhs.expression as Reference - assertEquals(-3, ((ifBranchCompRhs.expression as Literal<*>).value as Long)) + val declRefExpr = ifBranchCompLhs.expression + assertIs(declRefExpr) assertLocalName("x", declRefExpr) - // TODO: declRefExpr.refersTo is null. Is that expected/intended? + assertLiteralValue(-3L, ifBranchCompRhs.expression) + assertNotNull(declRefExpr.refersTo) - val ifBranchSecondStatement = ifBranch.statements[1] as? IfStatement - assertNotNull(ifBranchSecondStatement) - val ifRet = ifBranchSecondStatement.thenStatement as? Block - assertNotNull(ifRet) + val ifBranchSecondStatement = thenBranch.statements[1] + assertIs(ifBranchSecondStatement) + val ifRet = ifBranchSecondStatement.thenStatement + assertIs(ifRet) assertEquals(1, ifRet.statements.size) assertEquals(" ret i32 1", ifRet.statements[0].code) } @@ -365,25 +388,34 @@ class LLVMIRLanguageFrontendTest { assertNotNull(atomicrmwStatement) // Check that the value is assigned to - val decl = (atomicrmwStatement.statements[0].declarations[0] as VariableDeclaration) - assertLocalName("old", decl) - assertLocalName("i32", decl.type) - assertEquals("*", (decl.initializer as UnaryOperator).operatorCode) - assertLocalName("ptr", (decl.initializer as UnaryOperator).input) + val declaration = atomicrmwStatement.statements[0].declarations[0] + assertIs(declaration) + assertLocalName("old", declaration) + assertLocalName("i32", declaration.type) + val initializer = declaration.initializer + assertIs(initializer) + assertEquals("*", initializer.operatorCode) + assertLocalName("ptr", initializer.input) // Check that the replacement equals *ptr = *ptr + 1 - val replacement = (atomicrmwStatement.statements[1] as AssignExpression) + val replacement = atomicrmwStatement.statements[1] + assertIs(replacement) assertEquals(1, replacement.lhs.size) assertEquals(1, replacement.rhs.size) assertEquals("=", replacement.operatorCode) - assertEquals("*", (replacement.lhs.first() as UnaryOperator).operatorCode) - assertLocalName("ptr", (replacement.lhs.first() as UnaryOperator).input) + val replacementLhs = replacement.lhs.first() + assertIs(replacementLhs) + assertEquals("*", replacementLhs.operatorCode) + assertLocalName("ptr", replacementLhs.input) // Check that the rhs is equal to *ptr + 1 - val add = replacement.rhs.first() as BinaryOperator + val add = replacement.rhs.first() + assertIs(add) assertEquals("+", add.operatorCode) - assertEquals("*", (add.lhs as UnaryOperator).operatorCode) - assertLocalName("ptr", (add.lhs as UnaryOperator).input) - assertEquals(1L, (add.rhs as Literal<*>).value as Long) + val addLhs = add.lhs + assertIs(addLhs) + assertEquals("*", addLhs.operatorCode) + assertLocalName("ptr", addLhs.input) + assertLiteralValue(1L, add.rhs) } @Test @@ -407,38 +439,53 @@ class LLVMIRLanguageFrontendTest { // Check that the first statement is "literal_i32_i1 val_success = literal_i32_i1(*ptr, *ptr // == 5)" - val decl = (cmpxchgStatement.statements[0].declarations[0] as VariableDeclaration) - assertLocalName("val_success", decl) - assertLocalName("literal_i32_i1", decl.type) + val declaration = cmpxchgStatement.statements[0].declarations[0] + assertIs(declaration) + assertLocalName("val_success", declaration) + assertLocalName("literal_i32_i1", declaration.type) // Check that the first value is *ptr - val value1 = (decl.initializer as ConstructExpression).arguments[0] as UnaryOperator + val declarationInitializer = declaration.initializer + assertIs(declarationInitializer) + val value1 = declarationInitializer.arguments[0] + assertIs(value1) assertEquals("*", value1.operatorCode) assertLocalName("ptr", value1.input) // Check that the first value is *ptr == 5 - val value2 = (decl.initializer as ConstructExpression).arguments[1] as BinaryOperator + val value2 = declarationInitializer.arguments[1] + assertIs(value2) assertEquals("==", value2.operatorCode) - assertEquals("*", (value2.lhs as UnaryOperator).operatorCode) - assertLocalName("ptr", (value2.lhs as UnaryOperator).input) - assertEquals(5L, (value2.rhs as Literal<*>).value as Long) - - val ifStatement = cmpxchgStatement.statements[1] as IfStatement + val value2Lhs = value2.lhs + assertIs(value2Lhs) + assertEquals("*", value2Lhs.operatorCode) + assertLocalName("ptr", value2Lhs.input) + assertLiteralValue(5L, value2.rhs) + + val ifStatement = cmpxchgStatement.statements[1] + assertIs(ifStatement) // The condition is the same as the second value above - val ifExpr = ifStatement.condition as BinaryOperator + val ifExpr = ifStatement.condition + assertIs(ifExpr) assertEquals("==", ifExpr.operatorCode) - assertEquals("*", (ifExpr.lhs as UnaryOperator).operatorCode) - assertLocalName("ptr", (ifExpr.lhs as UnaryOperator).input) - assertEquals(5L, (ifExpr.rhs as Literal<*>).value as Long) - - val thenExpr = ifStatement.thenStatement as AssignExpression + val ifExprLhs = ifExpr.lhs + assertIs(ifExprLhs) + assertEquals("*", ifExprLhs.operatorCode) + assertLocalName("ptr", ifExprLhs.input) + assertLiteralValue(5L, ifExpr.rhs) + + val thenExpr = ifStatement.thenStatement + assertIs(thenExpr) assertEquals(1, thenExpr.lhs.size) assertEquals(1, thenExpr.rhs.size) assertEquals("=", thenExpr.operatorCode) - assertEquals("*", (thenExpr.lhs.first() as UnaryOperator).operatorCode) - assertLocalName("ptr", (thenExpr.lhs.first() as UnaryOperator).input) - assertLocalName("old", thenExpr.rhs.first() as Reference) - assertLocalName("old", (thenExpr.rhs.first() as Reference).refersTo) + val thenExprLhs = thenExpr.lhs.first() + assertIs(thenExprLhs) + assertEquals("*", thenExprLhs.operatorCode) + assertLocalName("ptr", thenExprLhs.input) + assertIs(thenExpr.rhs.first()) + assertLocalName("old", thenExpr.rhs.first()) + assertRefersTo(thenExpr.rhs.first(), tu.variables["old"]) } @Test @@ -456,13 +503,15 @@ class LLVMIRLanguageFrontendTest { val foo = tu.functions["foo"] assertNotNull(foo) - val decl = foo.variables["value_loaded"] - assertNotNull(decl) - assertLocalName("i1", decl.type) + val declaration = foo.variables["value_loaded"] + assertNotNull(declaration) + assertLocalName("i1", declaration.type) - assertLocalName("val_success", (decl.initializer as MemberExpression).base) - assertEquals(".", (decl.initializer as MemberExpression).operatorCode) - assertLocalName("field_1", decl.initializer as MemberExpression) + val initializer = declaration.initializer + assertIs(initializer) + assertLocalName("val_success", initializer.base) + assertEquals(".", initializer.operatorCode) + assertLocalName("field_1", initializer) } @Test @@ -481,28 +530,26 @@ class LLVMIRLanguageFrontendTest { val foo = tu.functions["foo"] assertNotNull(foo) - assertEquals("literal_i32_i8", foo.type.typeName) + val fooType = foo.type + assertIs(fooType) + assertEquals("literal_i32_i8", fooType.typeName) - val record = (foo.type as? ObjectType)?.recordDeclaration + val record = fooType.recordDeclaration assertNotNull(record) assertEquals(2, record.fields.size) - val returnStatement = foo.bodyOrNull(0) + val returnStatement = foo.returns.singleOrNull() assertNotNull(returnStatement) - val construct = returnStatement.returnValue as? ConstructExpression - assertNotNull(construct) + val construct = returnStatement.returnValue + assertIs(construct) assertEquals(2, construct.arguments.size) - var arg = construct.arguments.getOrNull(0) as? Literal<*> - assertNotNull(arg) - assertEquals("i32", arg.type.typeName) - assertEquals(4L, arg.value) + assertEquals("i32", construct.arguments[0].type.typeName) + assertLiteralValue(4L, construct.arguments[0]) - arg = construct.arguments.getOrNull(1) as? Literal<*> - assertNotNull(arg) - assertEquals("i8", arg.type.typeName) - assertEquals(2L, arg.value) + assertEquals("i8", construct.arguments[1].type.typeName) + assertLiteralValue(2L, construct.arguments[1]) } @Test @@ -534,26 +581,30 @@ class LLVMIRLanguageFrontendTest { assertNotNull(loadXStatement) assertLocalName("locX", loadXStatement.singleDeclaration) - val initXOp = - (loadXStatement.singleDeclaration as VariableDeclaration).initializer as UnaryOperator + val initXOpDeclaration = loadXStatement.singleDeclaration + assertIs(initXOpDeclaration) + val initXOp = initXOpDeclaration.initializer + assertIs(initXOp) assertEquals("*", initXOp.operatorCode) - var ref = initXOp.input as? Reference - assertNotNull(ref) + var ref = initXOp.input + assertIs(ref) assertLocalName("x", ref) - assertSame(globalX, ref.refersTo) + assertRefersTo(ref, globalX) val loadAStatement = main.bodyOrNull(2) assertNotNull(loadAStatement) + val loadADeclaration = loadAStatement.singleDeclaration + assertIs(loadADeclaration) assertLocalName("locA", loadAStatement.singleDeclaration) - val initAOp = - (loadAStatement.singleDeclaration as VariableDeclaration).initializer as UnaryOperator + val initAOp = loadADeclaration.initializer + assertIs(initAOp) assertEquals("*", initAOp.operatorCode) - ref = initAOp.input as? Reference - assertNotNull(ref) + ref = initAOp.input + assertIs(ref) assertLocalName("a", ref) - assertSame(globalA, ref.refersTo) + assertRefersTo(ref, globalA) } @Test @@ -570,11 +621,11 @@ class LLVMIRLanguageFrontendTest { assertNotNull(main) // %ptr = alloca i32 - val ptr = main.bodyOrNull()?.singleDeclaration as? VariableDeclaration - assertNotNull(ptr) + val ptr = main.bodyOrNull()?.singleDeclaration + assertIs(ptr) - val alloca = ptr.initializer as? NewArrayExpression - assertNotNull(alloca) + val alloca = ptr.initializer + assertIs(alloca) assertEquals("i32*", alloca.type.typeName) // store i32 3, i32* %ptr @@ -583,16 +634,16 @@ class LLVMIRLanguageFrontendTest { assertEquals("=", store.operatorCode) assertEquals(1, store.lhs.size) - val dereferencePtr = store.lhs.first() as? UnaryOperator - assertNotNull(dereferencePtr) + val dereferencePtr = store.lhs.firstOrNull() + assertIs(dereferencePtr) assertEquals("*", dereferencePtr.operatorCode) assertEquals("i32", dereferencePtr.type.typeName) - assertSame(ptr, (dereferencePtr.input as? Reference)?.refersTo) + assertRefersTo(dereferencePtr.input, ptr) assertEquals(1, store.rhs.size) - val value = store.rhs.first() as? Literal<*> - assertNotNull(value) - assertEquals(3L, value.value) + val value = store.rhs.firstOrNull() + assertIs>(value) + assertLiteralValue(3L, value) assertEquals("i32", value.type.typeName) } @@ -614,17 +665,22 @@ class LLVMIRLanguageFrontendTest { assertNotNull(foo) assertEquals("literal_i32_i8", foo.type.typeName) - val record = (foo.type as? ObjectType)?.recordDeclaration + val fooType = foo.type + assertIs(fooType) + val record = fooType.recordDeclaration assertNotNull(record) assertEquals(2, record.fields.size) - val declStatement = foo.bodyOrNull() - assertNotNull(declStatement) + val declarationStatement = foo.bodyOrNull() + assertNotNull(declarationStatement) - val varDecl = declStatement.singleDeclaration as VariableDeclaration - assertLocalName("a", varDecl) - assertEquals("literal_i32_i8", varDecl.type.typeName) - val args = (varDecl.initializer as ConstructExpression).arguments + val varDeclaration = declarationStatement.singleDeclaration + assertIs(varDeclaration) + assertLocalName("a", varDeclaration) + assertEquals("literal_i32_i8", varDeclaration.type.typeName) + val initializer = varDeclaration.initializer + assertIs(initializer) + val args = initializer.arguments assertEquals(2, args.size) assertLiteralValue(100L, args[0]) assertLiteralValue(null, args[1]) @@ -645,10 +701,12 @@ class LLVMIRLanguageFrontendTest { assertEquals("=", assign.operatorCode) assertEquals(1, assign.lhs.size) assertEquals(1, assign.rhs.size) - assertLocalName("b", (assign.lhs.first() as MemberExpression).base) - assertEquals(".", (assign.lhs.first() as MemberExpression).operatorCode) - assertLocalName("field_1", assign.lhs.first() as MemberExpression) - assertEquals(7L, (assign.rhs.first() as Literal<*>).value as Long) + val assignLhs = assign.lhs.first() + assertIs(assignLhs) + assertLocalName("b", assignLhs.base) + assertEquals(".", assignLhs.operatorCode) + assertLocalName("field_1", assignLhs) + assertLiteralValue(7L, assign.rhs.first()) } @Test @@ -668,41 +726,44 @@ class LLVMIRLanguageFrontendTest { val main = tu.functions["main"] assertNotNull(main) - val mainBody = main.body as Block - val tryStatement = mainBody.statements[0] as? TryStatement - assertNotNull(tryStatement) + val mainBody = main.body + assertIs(mainBody) + val tryStatement = mainBody.statements[0] + assertIs(tryStatement) // Check the assignment of the function call - val resDecl = - (tryStatement.tryBlock?.statements?.get(0) as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration - assertNotNull(resDecl) - assertLocalName("res", resDecl) - val call = resDecl.initializer as? CallExpression - assertNotNull(call) + val resDeclarationStatement = tryStatement.tryBlock?.statements?.get(0) + assertIs(resDeclarationStatement) + val resDeclaration = resDeclarationStatement.singleDeclaration + assertIs(resDeclaration) + assertLocalName("res", resDeclaration) + val call = resDeclaration.initializer + assertIs(call) assertLocalName("throwingFoo", call) - assertTrue(call.invokes.contains(throwingFoo)) + assertContains(call.invokes, throwingFoo) assertEquals(0, call.arguments.size) // Check that the second part of the try-block is inlined by the pass - val aDecl = - (tryStatement.tryBlock?.statements?.get(1) as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration - assertNotNull(aDecl) - assertLocalName("a", aDecl) - val resStatement = tryStatement.tryBlock?.statements?.get(2) as? ReturnStatement - assertNotNull(resStatement) + val aDeclarationStatement = tryStatement.tryBlock?.statements?.get(1) + assertIs(aDeclarationStatement) + val aDeclaration = aDeclarationStatement.singleDeclaration + assertIs(aDeclaration) + assertLocalName("a", aDeclaration) + val resStatement = tryStatement.tryBlock?.statements?.get(2) + assertIs(resStatement) // Check that the catch block is inlined by the pass assertEquals(1, tryStatement.catchClauses.size) assertEquals(5, tryStatement.catchClauses[0].body?.statements?.size) assertLocalName("_ZTIi | ...", tryStatement.catchClauses[0]) - val ifStatement = tryStatement.catchClauses[0].body?.statements?.get(4) as? IfStatement - assertNotNull(ifStatement) - assertTrue(ifStatement.thenStatement is Block) - assertEquals(4, (ifStatement.thenStatement as Block).statements.size) - assertTrue(ifStatement.elseStatement is Block) - assertEquals(1, (ifStatement.elseStatement as Block).statements.size) + val ifStatement = tryStatement.catchClauses[0].body?.statements?.get(4) + assertIs(ifStatement) + val thenStatement = ifStatement.thenStatement + assertIs(thenStatement) + assertEquals(4, thenStatement.statements.size) + val elseStatement = ifStatement.elseStatement + assertIs(elseStatement) + assertEquals(1, elseStatement.statements.size) } @Test @@ -726,48 +787,52 @@ class LLVMIRLanguageFrontendTest { val main = tu.functions["main"] assertNotNull(main) - val mainBody = main.body as Block - val yDecl = - (mainBody.statements[0] as DeclarationStatement).singleDeclaration - as VariableDeclaration - assertNotNull(yDecl) + val mainBody = main.body + assertIs(mainBody) + val yDeclarationStatement = mainBody.statements[0] + assertIs(yDeclarationStatement) + val yDecl = yDeclarationStatement.singleDeclaration + assertIs(yDecl) - val ifStatement = mainBody.statements[3] as? IfStatement - assertNotNull(ifStatement) + val ifStatement = mainBody.statements[3] + assertIs(ifStatement) - val thenStmt = ifStatement.thenStatement as? Block - assertNotNull(thenStmt) + val thenStmt = ifStatement.thenStatement + assertIs(thenStmt) assertEquals(3, thenStmt.statements.size) - assertNotNull(thenStmt.statements[1] as? AssignExpression) - val aDecl = - (thenStmt.statements[0] as DeclarationStatement).singleDeclaration - as VariableDeclaration - val thenY = thenStmt.statements[1] as AssignExpression + val aDeclarationStatement = thenStmt.statements[0] + assertIs(aDeclarationStatement) + val aDecl = aDeclarationStatement.singleDeclaration + assertIs(aDecl) + val thenY = thenStmt.statements[1] + assertIs(thenY) assertEquals(1, thenY.lhs.size) assertEquals(1, thenY.rhs.size) - assertSame(aDecl, (thenY.rhs.first() as Reference).refersTo) - assertSame(yDecl, (thenY.lhs.first() as Reference).refersTo) + assertRefersTo(thenY.rhs.first(), aDecl) + assertRefersTo(thenY.lhs.first(), yDecl) - val elseStmt = ifStatement.elseStatement as? Block - assertNotNull(elseStmt) + val elseStmt = ifStatement.elseStatement + assertIs(elseStmt) assertEquals(3, elseStmt.statements.size) - val bDecl = - (elseStmt.statements[0] as DeclarationStatement).singleDeclaration - as VariableDeclaration - assertNotNull(elseStmt.statements[1] as? AssignExpression) - val elseY = elseStmt.statements[1] as AssignExpression + val bDeclarationStatement = elseStmt.statements[0] + assertIs(bDeclarationStatement) + val bDecl = bDeclarationStatement.singleDeclaration + assertIs(bDecl) + val elseY = elseStmt.statements[1] + assertIs(elseY) assertEquals(1, elseY.lhs.size) assertEquals(1, elseY.lhs.size) - assertSame(bDecl, (elseY.rhs.first() as Reference).refersTo) - assertSame(yDecl, (elseY.lhs.first() as Reference).refersTo) - - val continueBlock = - (thenStmt.statements[2] as? GotoStatement)?.targetLabel?.subStatement as? Block - assertNotNull(continueBlock) - assertEquals( - yDecl, - ((continueBlock.statements[1] as ReturnStatement).returnValue as Reference).refersTo - ) + assertRefersTo(elseY.rhs.first(), bDecl) + assertRefersTo(elseY.lhs.first(), yDecl) + + val gotoStatement = thenStmt.statements[2] + assertIs(gotoStatement) + val continueBlock = gotoStatement.targetLabel?.subStatement + assertIs(continueBlock) + val returnStatement = continueBlock.statements[1] + assertIs(returnStatement) + assertIs(returnStatement.returnValue) + assertRefersTo(returnStatement.returnValue, yDecl) } @Test @@ -781,99 +846,85 @@ class LLVMIRLanguageFrontendTest { assertNotNull(main) // Test that x is initialized correctly - val mainBody = main.body as Block - val origX = - ((mainBody.statements[0] as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration) - val xInit = origX?.initializer as? InitializerListExpression - assertNotNull(xInit) - assertEquals(10L, (xInit.initializers[0] as? Literal<*>)?.value) - assertEquals(9L, (xInit.initializers[1] as? Literal<*>)?.value) - assertEquals(6L, (xInit.initializers[2] as? Literal<*>)?.value) - assertEquals(-100L, (xInit.initializers[3] as? Literal<*>)?.value) + val mainBody = main.body + assertIs(mainBody) + val xDeclarationStatement = mainBody.statements[0] + assertIs(xDeclarationStatement) + val origX = xDeclarationStatement.singleDeclaration + assertIs(origX) + val xInit = origX.initializer + assertIs(xInit) + assertLiteralValue(10L, xInit.initializers[0]) + assertLiteralValue(9L, xInit.initializers[1]) + assertLiteralValue(6L, xInit.initializers[2]) + assertLiteralValue(-100L, xInit.initializers[3]) // Test that y is initialized correctly - val origY = - ((mainBody.statements[1] as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration) - val yInit = origY?.initializer as? InitializerListExpression - assertNotNull(yInit) - assertEquals(15L, (yInit.initializers[0] as? Literal<*>)?.value) - assertEquals(34L, (yInit.initializers[1] as? Literal<*>)?.value) - assertEquals(99L, (yInit.initializers[2] as? Literal<*>)?.value) - assertEquals(1000L, (yInit.initializers[3] as? Literal<*>)?.value) + + val yDeclarationStatement = mainBody.statements[1] + assertIs(yDeclarationStatement) + val origY = yDeclarationStatement.singleDeclaration + assertIs(origY) + val yInit = origY.initializer + assertIs(yInit) + assertLiteralValue(15L, yInit.initializers[0]) + assertLiteralValue(34L, yInit.initializers[1]) + assertLiteralValue(99L, yInit.initializers[2]) + assertLiteralValue(1000L, yInit.initializers[3]) // Test that extractelement works - val zInit = - ((mainBody.statements[2] as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration) - ?.initializer as? SubscriptExpression - assertNotNull(zInit) - assertEquals(0L, (zInit.subscriptExpression as? Literal<*>)?.value) - assertEquals("x", (zInit.arrayExpression as? Reference)?.name?.localName) - assertSame(origX, (zInit.arrayExpression as? Reference)?.refersTo) + val zDeclarationStatement = mainBody.statements[2] + assertIs(zDeclarationStatement) + val origZ = zDeclarationStatement.singleDeclaration + assertIs(origZ) + val zInit = origZ.initializer + assertIs(zInit) + assertLiteralValue(0L, zInit.subscriptExpression) + assertLocalName("x", zInit.arrayExpression) + assertRefersTo(zInit.arrayExpression, origX) // Test the assignment of y to yMod - val yModInit = - ((mainBody.statements[3] as Block).statements[0] as? DeclarationStatement) - ?.singleDeclaration as? VariableDeclaration - assertNotNull(yModInit) - assertEquals("y", (yModInit.initializer as? Reference)?.name?.localName) - assertSame(origY, (yModInit.initializer as? Reference)?.refersTo) + val yModDeclarationStatementBlock = mainBody.statements[3] + assertIs(yModDeclarationStatementBlock) + val yModDeclarationStatement = yModDeclarationStatementBlock.statements[0] + assertIs(yModDeclarationStatement) + val modY = yModDeclarationStatement.singleDeclaration + assertIs(modY) + val yModInit = modY.initializer + assertIs(yModInit) + assertLocalName("y", yModInit) + assertRefersTo(yModInit, origY) + // Now, test the modification of yMod[3] = 8 - val yMod = ((mainBody.statements[3] as Block).statements[1] as? AssignExpression) - assertNotNull(yMod) + val yMod = yModDeclarationStatementBlock.statements[1] + assertIs(yMod) assertEquals(1, yMod.lhs.size) assertEquals(1, yMod.rhs.size) - assertEquals( - 3L, - ((yMod.lhs.first() as? SubscriptExpression)?.subscriptExpression as? Literal<*>)?.value - ) - assertSame( - yModInit, - ((yMod.lhs.first() as? SubscriptExpression)?.arrayExpression as? Reference)?.refersTo - ) - assertEquals(8L, (yMod.rhs.first() as? Literal<*>)?.value) + val yModLhs = yMod.lhs.first() + assertIs(yModLhs) + assertLiteralValue(3L, yModLhs.subscriptExpression) + assertRefersTo(yModLhs.arrayExpression, modY) + assertLiteralValue(8L, yMod.rhs.first()) // Test the last shufflevector instruction which does not contain constant as initializers. - val shuffledInit = - ((mainBody.statements[4] as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration) - ?.initializer as? InitializerListExpression - assertNotNull(shuffledInit) - assertSame( - origX, - ((shuffledInit.initializers[0] as? SubscriptExpression)?.arrayExpression as? Reference) - ?.refersTo - ) - assertSame( - yModInit, - ((shuffledInit.initializers[1] as? SubscriptExpression)?.arrayExpression as? Reference) - ?.refersTo - ) - assertSame( - yModInit, - ((shuffledInit.initializers[2] as? SubscriptExpression)?.arrayExpression as? Reference) - ?.refersTo - ) - assertSame( - 1, - ((shuffledInit.initializers[0] as? SubscriptExpression)?.subscriptExpression - as? Literal<*>) - ?.value - ) - assertSame( - 2, - ((shuffledInit.initializers[1] as? SubscriptExpression)?.subscriptExpression - as? Literal<*>) - ?.value - ) - assertSame( - 3, - ((shuffledInit.initializers[2] as? SubscriptExpression)?.subscriptExpression - as? Literal<*>) - ?.value - ) + val shuffledInitDeclarationStatement = mainBody.statements[4] + assertIs(shuffledInitDeclarationStatement) + val shuffledInitDeclaration = shuffledInitDeclarationStatement.singleDeclaration + assertIs(shuffledInitDeclaration) + val shuffledInit = shuffledInitDeclaration.initializer + assertIs(shuffledInit) + val shuffledInit0 = shuffledInit.initializers[0] + assertIs(shuffledInit0) + val shuffledInit1 = shuffledInit.initializers[1] + assertIs(shuffledInit1) + val shuffledInit2 = shuffledInit.initializers[2] + assertIs(shuffledInit2) + assertRefersTo(shuffledInit0.arrayExpression, origX) + assertRefersTo(shuffledInit1.arrayExpression, modY) + assertRefersTo(shuffledInit2.arrayExpression, modY) + assertLiteralValue(1, shuffledInit0.subscriptExpression) + assertLiteralValue(2, shuffledInit1.subscriptExpression) + assertLiteralValue(3, shuffledInit2.subscriptExpression) } @Test @@ -887,19 +938,20 @@ class LLVMIRLanguageFrontendTest { assertNotNull(main) // Test that x is initialized correctly - val mainBody = main.body as Block + val mainBody = main.body + assertIs(mainBody) - val fenceCall = mainBody.statements[0] as? CallExpression - assertNotNull(fenceCall) + val fenceCall = mainBody.statements[0] + assertIs(fenceCall) assertEquals(1, fenceCall.arguments.size) - assertEquals(2, (fenceCall.arguments[0] as Literal<*>).value) + assertLiteralValue(2, fenceCall.arguments[0]) - val fenceCallScope = mainBody.statements[2] as? CallExpression - assertNotNull(fenceCallScope) + val fenceCallScope = mainBody.statements[2] + assertIs(fenceCallScope) assertEquals(2, fenceCallScope.arguments.size) // TODO: This doesn't match but it doesn't seem to be our mistake // assertEquals(5, (fenceCallScope.arguments[0] as Literal<*>).value) - assertEquals("scope", (fenceCallScope.arguments[1] as Literal<*>).value) + assertLiteralValue("scope", fenceCallScope.arguments[1]) } @Test @@ -917,26 +969,25 @@ class LLVMIRLanguageFrontendTest { val funcF = tu.functions["f"] assertNotNull(funcF) - val tryStatement = - (funcF.bodyOrNull(0)?.subStatement as? Block) - ?.statements - ?.firstOrNull { s -> s is TryStatement } - assertIs(tryStatement) - assertEquals(2, tryStatement.tryBlock?.statements?.size) - assertFullName( - "_CxxThrowException", - tryStatement.tryBlock?.statements?.get(0) as? CallExpression - ) - assertLocalName( - "end", - (tryStatement.tryBlock?.statements?.get(1) as? GotoStatement)?.targetLabel - ) + val tryStatement = funcF.bodyOrNull(0)?.subStatement?.trys?.firstOrNull() + assertNotNull(tryStatement) + val tryBlock = tryStatement.tryBlock + assertNotNull(tryBlock) + assertEquals(2, tryBlock.statements.size) + assertIs(tryBlock.statements[0]) + assertFullName("_CxxThrowException", tryBlock.statements[0]) + val gotoStatement = tryBlock.statements[1] + assertIs(gotoStatement) + assertLocalName("end", gotoStatement.targetLabel) assertEquals(1, tryStatement.catchClauses.size) - val catchSwitchExpr = tryStatement.catchClauses[0].body?.statements?.get(0) + val catchBody = tryStatement.catchClauses[0].body + assertNotNull(catchBody) + val catchSwitchExpr = catchBody.statements[0] assertIs(catchSwitchExpr) - val catchswitchCall = - (catchSwitchExpr.singleDeclaration as? VariableDeclaration)?.initializer + val catchSwitchDeclaration = catchSwitchExpr.singleDeclaration + assertIs(catchSwitchDeclaration) + val catchswitchCall = catchSwitchDeclaration.initializer assertIs(catchswitchCall) assertFullName("llvm.catchswitch", catchswitchCall) val ifExceptionMatches = tryStatement.catchClauses[0].body?.statements?.get(1) @@ -944,47 +995,47 @@ class LLVMIRLanguageFrontendTest { val matchesExceptionCall = ifExceptionMatches.condition assertIs(matchesExceptionCall) assertFullName("llvm.matchesCatchpad", matchesExceptionCall) - assertEquals( - catchSwitchExpr.singleDeclaration, - (matchesExceptionCall.arguments[0] as Reference).refersTo - ) - assertEquals(null, (matchesExceptionCall.arguments[1] as Literal<*>).value) - assertEquals(64L, (matchesExceptionCall.arguments[2] as Literal<*>).value as Long) - assertEquals(null, (matchesExceptionCall.arguments[3] as Literal<*>).value) + assertRefersTo(matchesExceptionCall.arguments[0], catchSwitchDeclaration) + assertLiteralValue(null, matchesExceptionCall.arguments[1]) + assertLiteralValue(64L, matchesExceptionCall.arguments[2]) + assertLiteralValue(null, matchesExceptionCall.arguments[3]) val catchBlock = ifExceptionMatches.thenStatement assertIs(catchBlock) - assertFullName( - "llvm.catchpad", - ((catchBlock.statements[0] as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration) - ?.initializer as? CallExpression - ) + val catchpadDeclarationStatement = catchBlock.statements[0] + assertIs(catchpadDeclarationStatement) + val catchpadDeclaration = catchpadDeclarationStatement.singleDeclaration + assertIs(catchpadDeclaration) + assertIs(catchpadDeclaration.initializer) + assertFullName("llvm.catchpad", catchpadDeclaration.initializer) val innerTry = catchBlock.statements[1] assertIs(innerTry) - assertFullName( - "_CxxThrowException", - innerTry.tryBlock?.statements?.get(0) as? CallExpression - ) - assertLocalName( - "try.cont", - (innerTry.tryBlock?.statements?.get(1) as? GotoStatement)?.targetLabel - ) - - val innerCatchClause = - (innerTry.catchClauses[0].body?.statements?.get(1) as? IfStatement)?.thenStatement + val innerTryBlock = innerTry.tryBlock + assertNotNull(innerTryBlock) + assertIs(innerTryBlock.statements[0]) + assertFullName("_CxxThrowException", innerTryBlock.statements[0]) + val innerTryGoto = innerTryBlock.statements[1] + assertIs(innerTryGoto) + assertLocalName("try.cont", innerTryGoto.targetLabel) + + val innerCatchBody = innerTry.catchClauses[0].body + assertNotNull(innerCatchBody) + val innerCatchIf = innerCatchBody.statements[1] + assertIs(innerCatchIf) + val innerCatchClause = innerCatchIf.thenStatement assertIs(innerCatchClause) - assertFullName( - "llvm.catchpad", - ((innerCatchClause.statements[0] as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration) - ?.initializer - ) - assertLocalName("try.cont", (innerCatchClause.statements[1] as? GotoStatement)?.targetLabel) - - val innerCatchThrows = - (innerTry.catchClauses[0].body?.statements?.get(1) as? IfStatement)?.elseStatement + val innerCatchpadDeclarationStatement = innerCatchClause.statements[0] + assertIs(innerCatchpadDeclarationStatement) + val innerCatchDeclaration = innerCatchpadDeclarationStatement.singleDeclaration + assertIs(innerCatchDeclaration) + assertFullName("llvm.catchpad", innerCatchDeclaration.initializer) + + val innerCatchGoto = innerCatchClause.statements[1] + assertIs(innerCatchGoto) + assertLocalName("try.cont", innerCatchGoto.targetLabel) + + val innerCatchThrows = innerCatchIf.elseStatement 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/ExpressionHandler.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt index 20dd22abb3..ac844775a2 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt @@ -364,7 +364,7 @@ class ExpressionHandler(frontend: PythonLanguageFrontend) : node.keys[i]?.let { handle(it) } ?: newProblemExpression("missing key"), value = handle(node.values[i]), ) - .codeAndLocationFromChildren(node) + .codeAndLocationFromChildren(node, frontend.lineSeparator) } val ile = newInitializerListExpression(rawNode = node) ile.type = frontend.objectType("dict") diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index dd56616062..98adf9ad7c 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -51,7 +51,7 @@ import kotlin.math.min @RegisterExtraPass(PythonAddDeclarationsPass::class) class PythonLanguageFrontend(language: Language, ctx: TranslationContext) : LanguageFrontend(language, ctx) { - private val lineSeparator = '\n' // TODO + val lineSeparator = "\n" // TODO private val tokenTypeIndex = 0 private val jep = JepSingleton // configure Jep @@ -191,7 +191,7 @@ class PythonLanguageFrontend(language: Language, ctx: Tr lines = removeExtraAtEnd(location, lines) lines = fixStartColumn(location, lines) - lines.joinToString(separator = lineSeparator.toString()) + lines.joinToString(separator = lineSeparator) } else { null } 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 a8ea976d29..cdb66e7103 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 @@ -1056,7 +1056,7 @@ class StatementHandler(frontend: PythonLanguageFrontend) : // and only Python.AST.BaseStmt nodes would be accepted. This would cause issues with // other nodes that are not "statements", but also handled as part of this handler, // e.g., the Python.AST.ExceptHandler. - with(frontend) { result.codeAndLocationFromChildren(ast) } + with(frontend) { result.codeAndLocationFromChildren(ast, frontend.lineSeparator) } } return result diff --git a/cpg-language-typescript/src/main/nodejs/package-lock.json b/cpg-language-typescript/src/main/nodejs/package-lock.json index 09652d2101..c3e47c71d1 100644 --- a/cpg-language-typescript/src/main/nodejs/package-lock.json +++ b/cpg-language-typescript/src/main/nodejs/package-lock.json @@ -6,7 +6,7 @@ "": { "license": "Apache-2.0", "dependencies": { - "@types/node": "^20.0.0", + "@types/node": "^22.0.0", "typescript": "5.6.2" }, "devDependencies": { @@ -152,9 +152,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz", + "integrity": "sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA==", "cpu": [ "arm" ], @@ -166,9 +166,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz", + "integrity": "sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg==", "cpu": [ "arm64" ], @@ -180,9 +180,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz", + "integrity": "sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg==", "cpu": [ "arm64" ], @@ -194,9 +194,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz", + "integrity": "sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA==", "cpu": [ "x64" ], @@ -207,10 +207,38 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz", + "integrity": "sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz", + "integrity": "sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz", + "integrity": "sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA==", "cpu": [ "arm" ], @@ -222,9 +250,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz", + "integrity": "sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ==", "cpu": [ "arm" ], @@ -236,9 +264,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz", + "integrity": "sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A==", "cpu": [ "arm64" ], @@ -250,9 +278,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz", + "integrity": "sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw==", "cpu": [ "arm64" ], @@ -264,9 +292,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz", + "integrity": "sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA==", "cpu": [ "ppc64" ], @@ -278,9 +306,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz", + "integrity": "sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA==", "cpu": [ "riscv64" ], @@ -292,9 +320,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz", + "integrity": "sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ==", "cpu": [ "s390x" ], @@ -306,9 +334,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz", + "integrity": "sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig==", "cpu": [ "x64" ], @@ -320,9 +348,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz", + "integrity": "sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ==", "cpu": [ "x64" ], @@ -334,9 +362,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz", + "integrity": "sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww==", "cpu": [ "arm64" ], @@ -348,9 +376,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz", + "integrity": "sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg==", "cpu": [ "ia32" ], @@ -362,9 +390,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz", + "integrity": "sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg==", "cpu": [ "x64" ], @@ -383,12 +411,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.1.tgz", - "integrity": "sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/@types/resolve": { @@ -546,9 +574,9 @@ } }, "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.25.0.tgz", + "integrity": "sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg==", "dev": true, "license": "MIT", "dependencies": { @@ -562,22 +590,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@rollup/rollup-android-arm-eabi": "4.25.0", + "@rollup/rollup-android-arm64": "4.25.0", + "@rollup/rollup-darwin-arm64": "4.25.0", + "@rollup/rollup-darwin-x64": "4.25.0", + "@rollup/rollup-freebsd-arm64": "4.25.0", + "@rollup/rollup-freebsd-x64": "4.25.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.25.0", + "@rollup/rollup-linux-arm-musleabihf": "4.25.0", + "@rollup/rollup-linux-arm64-gnu": "4.25.0", + "@rollup/rollup-linux-arm64-musl": "4.25.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.25.0", + "@rollup/rollup-linux-riscv64-gnu": "4.25.0", + "@rollup/rollup-linux-s390x-gnu": "4.25.0", + "@rollup/rollup-linux-x64-gnu": "4.25.0", + "@rollup/rollup-linux-x64-musl": "4.25.0", + "@rollup/rollup-win32-arm64-msvc": "4.25.0", + "@rollup/rollup-win32-ia32-msvc": "4.25.0", + "@rollup/rollup-win32-x64-msvc": "4.25.0", "fsevents": "~2.3.2" } }, @@ -614,9 +644,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.6.tgz", - "integrity": "sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "license": "MIT" } } diff --git a/cpg-language-typescript/src/main/nodejs/package.json b/cpg-language-typescript/src/main/nodejs/package.json index 5edee969eb..f380846c4e 100644 --- a/cpg-language-typescript/src/main/nodejs/package.json +++ b/cpg-language-typescript/src/main/nodejs/package.json @@ -5,7 +5,7 @@ "start": "node src/parser.js" }, "dependencies": { - "@types/node": "^20.0.0", + "@types/node": "^22.0.0", "typescript": "5.6.2" }, "license": "Apache-2.0", diff --git a/docs/docs/CPG/specs/eog.md b/docs/docs/CPG/specs/eog.md index 4dc34fa6e5..5c3a662a4d 100644 --- a/docs/docs/CPG/specs/eog.md +++ b/docs/docs/CPG/specs/eog.md @@ -357,26 +357,6 @@ flowchart LR child --EOG-->parent parent(["UnaryOperator"]) --EOG--> next:::outer parent -."statements(n)".-> child - -``` - - -### UnaryOperator for exception throws -Throwing of exceptions is modelled as unary operation. The EOG continues at an exception catching structure or a function that does a re-throw. - -Interesting fields: - -* `input: Expression`: Exception to be thrown for exception handling. - -Scheme: -```mermaid -flowchart LR - classDef outer fill:#fff,stroke:#ddd,stroke-dasharray:5 5; - prev:::outer --EOG--> child["input"] - child --EOG-->parent - parent(["throw"]) --EOG--> catchingContext:::outer - parent -."statements(n)".-> child - ``` ## ThrowExpression @@ -397,7 +377,6 @@ flowchart LR parent(["ThrowExpression"]) --EOG--> catchingContext:::outer parent -.-> child1 parent -.-> child2 - ```