diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/MultiValueEvaluator.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/MultiValueEvaluator.kt index 56fb4c9348..471bb78259 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/MultiValueEvaluator.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/MultiValueEvaluator.kt @@ -32,16 +32,10 @@ import de.fraunhofer.aisec.cpg.graph.invoke import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement import de.fraunhofer.aisec.cpg.graph.statements.ForStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass -import de.fraunhofer.aisec.cpg.passes.astParent import org.slf4j.Logger import org.slf4j.LoggerFactory -/** - * This [ValueEvaluator] can resolve multiple possible values of a node. - * - * It requires running the [EdgeCachePass] after the translation to add all necessary edges. - */ +/** This [ValueEvaluator] can resolve multiple possible values of a node. */ class MultiValueEvaluator : ValueEvaluator() { companion object { const val MAX_DEPTH: Int = 20 @@ -268,7 +262,8 @@ class MultiValueEvaluator : ValueEvaluator() { forStatement.initializerStatement == node || // The node is the initialization (initializerDecl != null && initializerDecl == - node.astParent) || // The parent of the node is the initializer of the loop + node.astParent) || // The parent of the node is the initializer of the + // loop // variable forStatement.iterationStatement == node || // The node or its parent are the iteration statement of the loop diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt index 27476e882c..a369fd78ac 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/fsm/DFAOrderEvaluator.kt @@ -33,7 +33,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference -import de.fraunhofer.aisec.cpg.passes.astParent import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt index 06753748bb..a89ed36095 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt @@ -33,7 +33,6 @@ import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.array import de.fraunhofer.aisec.cpg.graph.builder.* -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass import de.fraunhofer.aisec.cpg.passes.UnreachableEOGPass class GraphExamples { @@ -45,7 +44,6 @@ class GraphExamples { .defaultPasses() .registerLanguage(TestLanguage(".")) .registerPass() - .registerPass() .build() ) = testFrontend(config).build { @@ -244,7 +242,6 @@ class GraphExamples { .defaultPasses() .registerLanguage(TestLanguage(".")) .registerPass() - .registerPass() .build() ) = testFrontend(config).build { diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt index 8f5ef62437..2c79d4ec8c 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt @@ -32,7 +32,6 @@ import de.fraunhofer.aisec.cpg.graph.array import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.newNewArrayExpression import de.fraunhofer.aisec.cpg.graph.pointer -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass class Query { companion object { @@ -99,7 +98,6 @@ class Query { TranslationConfiguration.builder() .defaultPasses() .registerLanguage(TestLanguage(".")) - .registerPass() .inferenceConfiguration(InferenceConfiguration.builder().enabled(false).build()) .build() ) = @@ -176,7 +174,6 @@ class Query { TranslationConfiguration.builder() .defaultPasses() .registerLanguage(TestLanguage(".")) - .registerPass() .inferenceConfiguration( InferenceConfiguration.builder().inferFunctions(false).build() ) @@ -255,7 +252,6 @@ class Query { TranslationConfiguration.builder() .defaultPasses() .registerLanguage(TestLanguage(".")) - .registerPass() .build() ) = testFrontend(config).build { @@ -392,7 +388,6 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerPass() .registerLanguage(TestLanguage(".")) .build() ) = @@ -438,7 +433,6 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerPass() .registerLanguage(TestLanguage(".")) .build() ) = @@ -498,7 +492,6 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerPass() .registerLanguage(TestLanguage(".")) .build() ) = diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt index 62a578e9b7..256ece4b8d 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt @@ -29,7 +29,6 @@ import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass import de.fraunhofer.aisec.cpg.passes.UnreachableEOGPass class ValueEvaluationTests { @@ -40,7 +39,6 @@ class ValueEvaluationTests { .defaultPasses() .registerLanguage(TestLanguage(".")) .registerPass() - .registerPass() .build() ) = testFrontend(config).build { @@ -100,7 +98,6 @@ class ValueEvaluationTests { .defaultPasses() .registerLanguage(TestLanguage(".")) .registerPass() - .registerPass() .build() ) = testFrontend(config).build { @@ -143,7 +140,6 @@ class ValueEvaluationTests { .defaultPasses() .registerLanguage(TestLanguage(".")) .registerPass() - .registerPass() .build() ) = testFrontend(config).build { @@ -249,7 +245,6 @@ class ValueEvaluationTests { .defaultPasses() .registerLanguage(TestLanguage(".")) .registerPass() - .registerPass() .build() ) = testFrontend(config).build { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt index 0a2a8a403a..05ace1a4dc 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt @@ -26,16 +26,18 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.helpers.MeasurementHolder import de.fraunhofer.aisec.cpg.helpers.StatisticsHolder import de.fraunhofer.aisec.cpg.passes.Pass import java.util.* import java.util.concurrent.ConcurrentHashMap +import org.neo4j.ogm.annotation.Relationship /** * The global (intermediate) result of the translation. A [LanguageFrontend] will initially populate @@ -52,11 +54,12 @@ class TranslationResult( var finalCtx: TranslationContext, ) : Node(), StatisticsHolder { + @Relationship("COMPONENTS") val componentEdges = astEdgesOf() /** * Entry points to the CPG: "SoftwareComponent" refer to programs, application, other "bundles" * of software. */ - @AST val components = mutableListOf() + val components by unwrapping(TranslationResult::componentEdges) /** * Scratch storage that can be used by passes to store additional information in this result. diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/FrontendUtils.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/FrontendUtils.kt index c262db3654..0994f418fb 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/FrontendUtils.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/FrontendUtils.kt @@ -137,13 +137,13 @@ class FrontendUtils { val smallestEnclosingNode = enclosingNodes.sortedWith(compareBy { it.code?.length ?: 10000 }).first() - val children = SubgraphWalker.getAstChildren(smallestEnclosingNode).toMutableList() + val children = smallestEnclosingNode.astChildren.toMutableList() // Because in GO we wrap all elements into a NamespaceDeclaration we have to extract the // natural children children.addAll( children.filterIsInstance().flatMap { namespace -> - SubgraphWalker.getAstChildren(namespace).filter { it !in children } + namespace.astChildren.filter { it !in children } } ) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/AST.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/AST.kt deleted file mode 100644 index 238a1535f3..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/AST.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph - -import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker - -/** - * Annotates single member variables of supertype [Node] or a collection of nodes to be part of the - * AST of the current [Node]. This is used to iterate over all AST sub-nodes with - * [SubgraphWalker.getAstChildren]. - */ -@Target(AnnotationTarget.FIELD) @Retention(AnnotationRetention.RUNTIME) annotation class AST diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt index 3a8e368bd9..f50f6d3b78 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt @@ -26,6 +26,9 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping +import org.neo4j.ogm.annotation.Relationship /** * A node which presents some kind of complete piece of software, e.g., an application, a library, @@ -35,8 +38,10 @@ import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration * entry points or interactions with other software. */ open class Component : Node() { + @Relationship("TRANSLATION_UNITS") + val translationUnitEdges = astEdgesOf() /** All translation units belonging to this application. */ - @AST val translationUnits: MutableList = mutableListOf() + val translationUnits by unwrapping(Component::translationUnitEdges) @Synchronized fun addTranslationUnit(tu: TranslationUnitDeclaration) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt index a427c26792..e24ce7ff18 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt @@ -206,7 +206,7 @@ fun LanguageProvider.newTupleDeclaration( // Also all our elements need to have an auto-type elements.forEach { it.type = autoType() } - node.elements = elements + node.elements = elements.toMutableList() node.initializer = initializer 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 c31fae0504..8b49b05c84 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 @@ -127,8 +127,8 @@ fun MetadataProvider.newAssignExpression( val node = AssignExpression() node.applyMetadata(this, operatorCode, rawNode, true) node.operatorCode = operatorCode - node.lhs = lhs - node.rhs = rhs + node.lhs = lhs.toMutableList() + node.rhs = rhs.toMutableList() log(node) @@ -207,8 +207,8 @@ fun MetadataProvider.newConditionalExpression( */ @JvmOverloads fun MetadataProvider.newKeyValueExpression( - key: Expression? = null, - value: Expression? = null, + key: Expression, + value: Expression, rawNode: Any? = null ): KeyValueExpression { val node = KeyValueExpression() 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 54deee38d8..a36e83367a 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 @@ -40,7 +40,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.WhileStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker -import de.fraunhofer.aisec.cpg.passes.astParent import kotlin.math.absoluteValue /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt index 01fdd695e2..95a222b500 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt @@ -34,6 +34,7 @@ import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edges.* +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.flows.ControlDependences import de.fraunhofer.aisec.cpg.graph.edges.flows.Dataflows import de.fraunhofer.aisec.cpg.graph.edges.flows.EvaluationOrders @@ -165,6 +166,8 @@ abstract class Node : var astChildren: List = listOf() get() = SubgraphWalker.getAstChildren(this) + @Transient var astParent: Node? = null + /** Virtual property for accessing [prevEOGEdges] without property edges. */ @PopulatedByPass(EvaluationOrderGraphPass::class) var prevEOG by unwrapping(Node::prevEOGEdges) @@ -251,7 +254,8 @@ abstract class Node : var argumentIndex = 0 /** List of annotations associated with that node. */ - @AST var annotations: MutableList = ArrayList() + @Relationship("ANNOTATIONS") var annotationEdges = astEdgesOf() + var annotations by unwrapping(Node::annotationEdges) /** * If a node should be removed from the graph, just removing it from the AST is not enough (see diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt index 82d7d16f3e..6b97cd923b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementHolder.kt @@ -50,22 +50,6 @@ interface StatementHolder : Holder { */ var statements: MutableList - /** - * Adds the specified statement to this statement holder. The statements have to be stored as a - * list of statements as we try to avoid adding new AST-nodes that do not exist, e.g. a code - * body to hold statements - * - * This only exists because of - * [de.fraunhofer.aisec.cpg.graph.statements.ForEachStatement.addStatement] which is needed - * until we re-design the Fluent DSL. - * - * @param s the statement - */ - @Deprecated(message = "This should be replaced by a direct call to statementEdges.add.") - fun addStatement(s: Statement) { - statementEdges.add(s) - } - override operator fun plusAssign(node: Statement) { statementEdges += 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 35178ca081..c3d2f87f79 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 @@ -1310,7 +1310,7 @@ fun Expression.conditional( context(LanguageFrontend<*, *>, StatementHolder) infix fun Expression.assign(init: AssignExpression.() -> Expression): AssignExpression { val node = (this@LanguageFrontend).newAssignExpression("=") - node.lhs = listOf(this) + node.lhs = mutableListOf(this) init(node) // node.rhs = listOf(init(node)) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumConstantDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumConstantDeclaration.kt index 3b2b8c7848..4ef95234bc 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumConstantDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumConstantDeclaration.kt @@ -25,14 +25,17 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasInitializer +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression +import org.neo4j.ogm.annotation.Relationship /** * Represents a constant within an [EnumDeclaration]. Depending on the language, this might have an * explicit initializer value. */ class EnumConstantDeclaration : ValueDeclaration(), HasInitializer { - @AST override var initializer: Expression? = null + @Relationship("INITIALIZER") var initializerEdge = astOptionalEdgeOf() + override var initializer by unwrapping(EnumConstantDeclaration::initializerEdge) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt index 322d8bdb58..c0e912c32f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/EnumDeclaration.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import org.apache.commons.lang3.builder.ToStringBuilder @@ -33,9 +32,7 @@ import org.neo4j.ogm.annotation.Relationship class EnumDeclaration : RecordDeclaration() { @Relationship(value = "ENTRIES", direction = Relationship.Direction.OUTGOING) - @AST var entryEdges = astEdgesOf() - var entries by unwrapping(EnumDeclaration::entryEdges) override fun toString(): String { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt index 2ee57000f5..f20968b641 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt @@ -28,6 +28,7 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block @@ -39,14 +40,13 @@ import org.neo4j.ogm.annotation.Relationship /** Represents the declaration or definition of a function. */ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, EOGStarterHolder { + @Relationship("BODY") var bodyEdge = astOptionalEdgeOf() /** The function body. Usually a [Block]. */ - @AST var body: Statement? = null + var body by unwrapping(FunctionDeclaration::bodyEdge) /** The list of function parameters. */ @Relationship(value = "PARAMETERS", direction = Relationship.Direction.OUTGOING) - @AST var parameterEdges = astEdgesOf() - /** Virtual property for accessing [parameterEdges] without property edges. */ var parameters by unwrapping(FunctionDeclaration::parameterEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt index 9563a79ed6..9b9bcdca32 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionTemplateDeclaration.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping @@ -40,7 +39,6 @@ class FunctionTemplateDeclaration : TemplateDeclaration() { * expansion pass for each instantiation of the FunctionTemplate there will be a realization */ @Relationship(value = "REALIZATION", direction = Relationship.Direction.OUTGOING) - @AST val realizationEdges = astEdgesOf() val realization by unwrapping(FunctionTemplateDeclaration::realizationEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt index ad8167ddca..da8e3edb14 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping @@ -36,12 +35,10 @@ import org.neo4j.ogm.annotation.Relationship /** This declaration represents either an include or an import, depending on the language. */ class IncludeDeclaration : Declaration() { @Relationship(value = "INCLUDES", direction = Relationship.Direction.OUTGOING) - @AST val includeEdges = astEdgesOf() val includes by unwrapping(IncludeDeclaration::includeEdges) @Relationship(value = "PROBLEMS", direction = Relationship.Direction.OUTGOING) - @AST val problemEdges = astEdgesOf() val problems by unwrapping(IncludeDeclaration::problemEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/MethodDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/MethodDeclaration.kt index 11578253b8..422e69fcef 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/MethodDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/MethodDeclaration.kt @@ -25,9 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import de.fraunhofer.aisec.cpg.passes.SymbolResolver +import org.neo4j.ogm.annotation.Relationship /** * A method declaration is a [FunctionDeclaration] that is part of to a specific [RecordDeclaration] @@ -41,6 +43,7 @@ open class MethodDeclaration : FunctionDeclaration() { */ open var recordDeclaration: RecordDeclaration? = null + @Relationship("RECEIVER") var receiverEdge = astOptionalEdgeOf() /** * The receiver variable of this method. In most cases, this variable is called `this`, but in * some languages, it is `self` (e.g. in Rust or Python) or can be freely named (e.g. in @@ -70,5 +73,5 @@ open class MethodDeclaration : FunctionDeclaration() { * share the same name. The [SymbolResolver] will recognize this and treat the scoping aspect of * the super-call accordingly. */ - @AST var receiver: VariableDeclaration? = null + var receiver by unwrapping(MethodDeclaration::receiverEdge) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt index 645aa8d65d..c2e9927eb5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/NamespaceDeclaration.kt @@ -48,11 +48,11 @@ class NamespaceDeclaration : Declaration(), DeclarationHolder, StatementHolder, * Edges to nested namespaces, records, functions, fields etc. contained in the current * namespace. */ - @AST override val declarations: MutableList = ArrayList() + val declarationEdges = astEdgesOf() + override val declarations by unwrapping(NamespaceDeclaration::declarationEdges) /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) - @AST override var statementEdges = astEdgesOf() /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt index a7ca01e17b..cd820498d0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt @@ -25,8 +25,9 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasDefault +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.* import org.neo4j.ogm.annotation.Relationship @@ -36,8 +37,8 @@ class ParameterDeclaration : ValueDeclaration(), HasDefault { var isVariadic = false @Relationship(value = "DEFAULT", direction = Relationship.Direction.OUTGOING) - @AST - private var defaultValue: Expression? = null + var defaultValueEdge = astOptionalEdgeOf() + private var defaultValue by unwrapping(ParameterDeclaration::defaultValueEdge) var modifiers: List = mutableListOf() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt index d603a94f90..86a9ceef33 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt @@ -44,33 +44,27 @@ open class RecordDeclaration : var kind: String? = null @Relationship(value = "FIELDS", direction = Relationship.Direction.OUTGOING) - @AST var fieldEdges = astEdgesOf() var fields by unwrapping(RecordDeclaration::fieldEdges) @Relationship(value = "METHODS", direction = Relationship.Direction.OUTGOING) - @AST var methodEdges = astEdgesOf() var methods by unwrapping(RecordDeclaration::methodEdges) @Relationship(value = "CONSTRUCTORS", direction = Relationship.Direction.OUTGOING) - @AST var constructorEdges = astEdgesOf() var constructors by unwrapping(RecordDeclaration::constructorEdges) @Relationship(value = "RECORDS", direction = Relationship.Direction.OUTGOING) - @AST var recordEdges = astEdgesOf() var records by unwrapping(RecordDeclaration::recordEdges) @Relationship(value = "TEMPLATES", direction = Relationship.Direction.OUTGOING) - @AST var templateEdges = astEdgesOf() var templates by unwrapping(RecordDeclaration::templateEdges) /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) - @AST override var statementEdges = astEdgesOf() override var statements by unwrapping(RecordDeclaration::statementEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt index ff2a74f08d..56e4b31645 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordTemplateDeclaration.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping @@ -40,7 +39,6 @@ class RecordTemplateDeclaration : TemplateDeclaration() { * expansion pass for each instantiation of the ClassTemplate there will be a realization */ @Relationship(value = "REALIZATION", direction = Relationship.Direction.OUTGOING) - @AST val realizationEdges = astEdgesOf() override val realizations by unwrapping(RecordTemplateDeclaration::realizationEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt index 11b887ad32..2b53dc4efa 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TemplateDeclaration.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.DeclarationHolder import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList @@ -52,7 +51,6 @@ abstract class TemplateDeclaration : Declaration(), DeclarationHolder { /** Parameters the Template requires for instantiation */ @Relationship(value = "PARAMETERS", direction = Relationship.Direction.OUTGOING) - @AST var parameterEdges = astEdgesOf() val parameters by unwrapping(TemplateDeclaration::parameterEdges) @@ -83,14 +81,6 @@ abstract class TemplateDeclaration : Declaration(), DeclarationHolder { return defaults } - fun addParameter(parameterizedType: TypeParameterDeclaration) { - parameterEdges.add(parameterizedType) - } - - fun addParameter(nonTypeTemplateParamDeclaration: ParameterDeclaration) { - parameterEdges.add(nonTypeTemplateParamDeclaration) - } - override val declarations: List get() { val list = ArrayList() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt index 91621691e1..99b6f5ea16 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TranslationUnitDeclaration.kt @@ -39,25 +39,21 @@ class TranslationUnitDeclaration : Declaration(), DeclarationHolder, StatementHolder, EOGStarterHolder { /** A list of declarations within this unit. */ @Relationship(value = "DECLARATIONS", direction = Relationship.Direction.OUTGOING) - @AST val declarationEdges = astEdgesOf() override val declarations by unwrapping(TranslationUnitDeclaration::declarationEdges) /** A list of includes within this unit. */ @Relationship(value = "INCLUDES", direction = Relationship.Direction.OUTGOING) - @AST val includeEdges = astEdgesOf() val includes by unwrapping(TranslationUnitDeclaration::includeEdges) /** A list of namespaces within this unit. */ @Relationship(value = "NAMESPACES", direction = Relationship.Direction.OUTGOING) - @AST val namespaceEdges = astEdgesOf() val namespaces by unwrapping(TranslationUnitDeclaration::namespaceEdges) /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) - @AST override var statementEdges = astEdgesOf() override var statements by unwrapping(TranslationUnitDeclaration::statementEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclaration.kt index 9d33d90930..acd7fe230f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclaration.kt @@ -25,8 +25,9 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.Name +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.newTupleDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.types.AutoType @@ -62,13 +63,12 @@ import de.fraunhofer.aisec.cpg.graph.types.TupleType */ class TupleDeclaration : VariableDeclaration() { /** The list of elements in this tuple. */ - @AST - var elements: List = mutableListOf() - set(value) { - field = value - // Make sure we inform our elements about our type changes - value.forEach { registerTypeObserver(it) } - } + var elementEdges = + astEdgesOf( + onAdd = { registerTypeObserver(it.end) }, + onRemove = { unregisterTypeObserver(it.end) } + ) + var elements by unwrapping(TupleDeclaration::elementEdges) override var name: Name get() = Name(elements.joinToString(",", "(", ")") { it.name.toString() }) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypeParameterDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypeParameterDeclaration.kt index 75e9f05d11..2f061836a2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypeParameterDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypeParameterDeclaration.kt @@ -25,18 +25,19 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasDefault +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* import org.neo4j.ogm.annotation.Relationship /** A declaration of a type template parameter */ class TypeParameterDeclaration : ValueDeclaration(), HasDefault { - /** TemplateParameters can define a default for the type parameter. */ @Relationship(value = "DEFAULT", direction = Relationship.Direction.OUTGOING) - @AST - override var default: Type? = null + var defaultEdge = astOptionalEdgeOf() + /** TemplateParameters can define a default for the type parameter. */ + override var default by unwrapping(TypeParameterDeclaration::defaultEdge) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt index 964ec5abc3..83fecda3e0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt @@ -26,6 +26,9 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression @@ -43,13 +46,10 @@ open class VariableDeclaration : ValueDeclaration(), HasInitializer, HasType.Typ /** * We need a way to store the templateParameters that a [VariableDeclaration] might have before * the [ConstructExpression] is created. - * - * Because templates are only used by a small subset of languages and variable declarations are - * used often, we intentionally make this a nullable list instead of an empty list. */ @Relationship(value = "TEMPLATE_PARAMETERS", direction = Relationship.Direction.OUTGOING) - @AST - var templateParameters: List? = null + var templateParameterEdges = astEdgesOf() + var templateParameters by unwrapping(VariableDeclaration::templateParameterEdges) /** Determines if this is a global variable. */ val isGlobal: Boolean @@ -66,17 +66,19 @@ open class VariableDeclaration : ValueDeclaration(), HasInitializer, HasType.Typ var isImplicitInitializerAllowed = false var isArray = false - /** The (optional) initializer of the declaration. */ - @AST - override var initializer: Expression? = null - set(value) { - field?.unregisterTypeObserver(this) - field = value - if (value is Reference) { - value.resolutionHelper = this + @Relationship("INITIALIZER") + var initializerEdge = + astOptionalEdgeOf( + onChanged = { old, new -> + val value = new?.end + exchangeTypeObserver(old, new) + if (value is Reference) { + value.resolutionHelper = this + } } - value?.registerTypeObserver(this) - } + ) + /** The (optional) initializer of the declaration. */ + override var initializer by unwrapping(VariableDeclaration::initializerEdge) fun getInitializerAs(clazz: Class): T? { return clazz.cast(initializer) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Edge.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Edge.kt index 34345f2811..1bb365dd63 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Edge.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Edge.kt @@ -34,6 +34,7 @@ import de.fraunhofer.aisec.cpg.graph.edges.flows.DependenceType import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.passes.ProgramDependenceGraphPass import java.util.* +import kotlin.reflect.KProperty import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.* @@ -50,7 +51,7 @@ import org.neo4j.ogm.annotation.* * ``` */ @RelationshipEntity -abstract class Edge : Persistable, Cloneable { +abstract class Edge : Persistable, Cloneable { /** Required field for object graph mapping. It contains the node id. */ @field:Id @field:GeneratedValue private val id: Long? = null @@ -58,14 +59,14 @@ abstract class Edge : Persistable, Cloneable { @JsonIgnore @field:StartNode var start: Node // Node where the edge is ingoing - @JsonBackReference @field:EndNode var end: T + @JsonBackReference @field:EndNode var end: NodeType - constructor(start: Node, end: T) { + constructor(start: Node, end: NodeType) { this.start = start this.end = end } - constructor(edge: Edge) { + constructor(edge: Edge) { start = edge.start end = edge.end } @@ -116,9 +117,9 @@ abstract class Edge : Persistable, Cloneable { .toString() } - public override fun clone(): Edge { + public override fun clone(): Edge { // needs to be implemented by sub-classes - return super.clone() as Edge + return super.clone() as Edge } companion object { @@ -143,4 +144,24 @@ abstract class Edge : Persistable, Cloneable { return false } } + + fun delegate(): Delegate { + return Delegate() + } + + @Transient + inner class Delegate< + ThisType : Node, + >() { + operator fun getValue(thisRef: ThisType, property: KProperty<*>): NodeType { + var edge = this@Edge + // We only support outgoing edges this way + return edge.end + } + + operator fun setValue(thisRef: ThisType, property: KProperty<*>, value: NodeType) { + this@Edge.end = value + // TODO: trigger some update hook + } + } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/EdgeWalker.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/EdgeWalker.kt new file mode 100644 index 0000000000..8aa453c0ee --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/EdgeWalker.kt @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edges + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeCollection +import de.fraunhofer.aisec.cpg.graph.edges.flows.Dataflow +import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker +import de.fraunhofer.aisec.cpg.helpers.identitySetOf + +/** + * Returns all [Edge]s of [EdgeType] directly attached to this [Node]. Optionally, a [predicate] can + * be used to filter the edges even further. + */ +inline fun > Node.edges( + noinline predicate: ((EdgeType) -> Boolean) = { true } +): Collection { + val edges = mutableSetOf() + val fields = SubgraphWalker.getAllEdgeFields(this::class.java) + for (field in fields) { + var obj = + synchronized(field) { + // Disable access mechanisms + field.isAccessible = true + val obj = field[this] + + // Restore old state + field.isAccessible = false + obj + } ?: continue + + // Gather all edges + if (obj is EdgeCollection<*, *>) { + for (edge in obj.toList()) { + if (edge is EdgeType && predicate.invoke(edge)) { + edges += edge + } + } + } + } + + return edges +} + +/** + * This function returns a subgraph containing all [Edge]s starting from this [Node] that are of the + * specific [EdgeType]. Optionally, a [predicate] can be used to filter the edges even further. + */ +inline fun > Node.allEdges( + noinline predicate: ((EdgeType) -> Boolean) = { true } +): Collection { + val alreadySeen = identitySetOf() + val worklist = mutableListOf() + val edges = mutableSetOf() + + worklist += this + alreadySeen += this + + while (worklist.isNotEmpty()) { + val node = worklist.removeFirst() + val toAdd = node.edges(predicate) + + val newStart = toAdd.map { it.start }.filter { it !in alreadySeen } + worklist += newStart + alreadySeen += newStart + + val newEnd = toAdd.map { it.end }.filter { it !in alreadySeen } + worklist += newEnd + alreadySeen += newEnd + + edges += toAdd + } + + return edges +} + +/** A shortcut to return all [AstEdge] edges starting from this node. */ +val Node.astEdges: Collection> + get() { + return allEdges() + } + +/** A shortcut to return all [Dataflow] edges starting from this node. */ +val Node.dataflows: Collection + get() { + return allEdges() + } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Extensions.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Extensions.kt index 5c2b2bc690..e880d20c8c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Extensions.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/Extensions.kt @@ -23,24 +23,23 @@ * \______/ \__| \______/ * */ -@file:Suppress("UNCHECKED_CAST") - package de.fraunhofer.aisec.cpg.graph.edges import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeList import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeSet +import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeSingletonList import de.fraunhofer.aisec.cpg.graph.edges.collections.UnwrappedEdgeList import de.fraunhofer.aisec.cpg.graph.edges.collections.UnwrappedEdgeSet import kotlin.reflect.KProperty1 import kotlin.reflect.jvm.isAccessible -@Suppress("UNCHECKED_CAST") fun > MutableList.add( target: Node, builder: EdgeType.() -> Unit ): Boolean { if (this is UnwrappedEdgeList<*, *>) { + @Suppress("UNCHECKED_CAST") return (this as UnwrappedEdgeList).add(target, builder) } @@ -66,3 +65,18 @@ fun > NodeTy val edge = edgeProperty.call(this) return edge.unwrap() } + +/** See [EdgeSingletonList.UnwrapDelegate]. */ +fun < + PropertyType : Node, + NullablePropertyType : PropertyType?, + NodeType : Node, + EdgeType : Edge +> NodeType.unwrapping( + edgeProperty: + KProperty1>, +): EdgeSingletonList.UnwrapDelegate { + edgeProperty.isAccessible = true + val edge = edgeProperty.call(this) + return edge.delegate() +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/ast/AstEdge.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/ast/AstEdge.kt index 298671fd37..adec108640 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/ast/AstEdge.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/ast/AstEdge.kt @@ -28,37 +28,70 @@ package de.fraunhofer.aisec.cpg.graph.edges.ast import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.edges.Edge import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeList +import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeSingletonList import org.neo4j.ogm.annotation.* /** This property edge describes a parent/child relationship in the Abstract Syntax Tree (AST). */ @RelationshipEntity -open class AstEdge : Edge { - constructor(start: Node, end: T) : super(start, end) { - // In a future PR, we will set the astParent here +open class AstEdge(start: Node, end: T) : Edge(start, end) { + init { + end.astParent = start } } /** Creates an [AstEdges] container starting from this node. */ fun Node.astEdgesOf( - postAdd: ((AstEdge) -> Unit)? = null, - postRemove: ((AstEdge) -> Unit)? = null, + onAdd: ((AstEdge) -> Unit)? = null, + onRemove: ((AstEdge) -> Unit)? = null, ): AstEdges> { - return AstEdges(this, postAdd, postRemove) + return AstEdges(thisRef = this, onAdd = onAdd, onRemove = onRemove) +} + +/** + * Creates an single optional [AstEdge] starting from this node (wrapped in a [EdgeSingletonList] + * container). + */ +fun Node.astOptionalEdgeOf( + onChanged: ((old: AstEdge?, new: AstEdge?) -> Unit)? = null, +): EdgeSingletonList> { + return EdgeSingletonList( + thisRef = this, + init = ::AstEdge, + outgoing = true, + onChanged = onChanged, + of = null + ) +} + +/** + * Creates an single [AstEdge] starting from this node (wrapped in a [EdgeSingletonList] container). + */ +fun Node.astEdgeOf( + of: NodeType, + onChanged: ((old: AstEdge?, new: AstEdge?) -> Unit)? = null, +): EdgeSingletonList> { + return EdgeSingletonList( + thisRef = this, + init = ::AstEdge, + outgoing = true, + onChanged = onChanged, + of = of + ) } /** This property edge list describes elements that are AST children of a node. */ -open class AstEdges>( +open class AstEdges>( thisRef: Node, - postAdd: ((PropertyEdgeType) -> Unit)? = null, - postRemove: ((PropertyEdgeType) -> Unit)? = null, + onAdd: ((PropertyEdgeType) -> Unit)? = null, + onRemove: ((PropertyEdgeType) -> Unit)? = null, @Suppress("UNCHECKED_CAST") init: (start: Node, end: NodeType) -> PropertyEdgeType = { start, end -> - AstEdge(start, end) as PropertyEdgeType + AstEdge(start, end) as PropertyEdgeType } ) : EdgeList( thisRef = thisRef, init = init, - postAdd = postAdd, - postRemove = postRemove, + onAdd = onAdd, + onRemove = onRemove, ) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeCollection.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeCollection.kt index 610d77a505..bf85360f05 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeCollection.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeCollection.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.graph.edges.collections import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.edges.Edge +import de.fraunhofer.aisec.cpg.graph.types.HasType.TypeObserver /** * This interfaces is an extension of [MutableCollection] that holds specific functions for the @@ -39,8 +40,8 @@ interface EdgeCollection< var thisRef: Node var init: (start: Node, end: NodeType) -> EdgeType var outgoing: Boolean - var postAdd: ((EdgeType) -> Unit)? - var postRemove: ((EdgeType) -> Unit)? + var onAdd: ((EdgeType) -> Unit)? + var onRemove: ((EdgeType) -> Unit)? /** * Removes all edges with the target node. The target is considered to be either the [Edge.end] @@ -55,10 +56,9 @@ interface EdgeCollection< it.start == target } } - return this.removeAll(toRemove) + return this.removeAll(toRemove.toSet()) } - @Suppress("UNCHECKED_CAST") fun addAll(targets: Collection, builder: (EdgeType.() -> Unit)? = null): Boolean { val edges = targets.map { @@ -66,7 +66,7 @@ interface EdgeCollection< if (outgoing) { init(thisRef, it) } else { - init(it, thisRef as NodeType) + @Suppress("UNCHECKED_CAST") init(it, thisRef as NodeType) } // Apply builder if (builder != null) { @@ -83,7 +83,6 @@ interface EdgeCollection< * If [outgoing] is true, the edge is created from [thisRef] -> [target], otherwise from * [target] to [thisRef]. */ - @Suppress("UNCHECKED_CAST") fun add( target: NodeType, init: ((Node, NodeType) -> EdgeType) = this.init, @@ -105,7 +104,7 @@ interface EdgeCollection< if (outgoing) { init(thisRef, target) } else { - init(target, thisRef as NodeType) + @Suppress("UNCHECKED_CAST") init(target, thisRef as NodeType) } // Apply builder @@ -139,7 +138,7 @@ interface EdgeCollection< * Note, that is an immutable list and only a snapshot. If you want a magic container that is in * sync with this [EdgeCollection], please use [unwrap]. */ - fun toNodeCollection(outgoing: Boolean = true): Collection + fun toNodeCollection(predicate: ((EdgeType) -> Boolean)? = null): Collection /** * Returns an [UnwrappedEdgeCollection] magic container which holds a structure that provides @@ -153,31 +152,40 @@ interface EdgeCollection< * propagate the edge to other properties or register additional handlers, e.g. a * [TypeObserver]. */ - fun handlePostAdd(edge: EdgeType) { - postAdd?.invoke(edge) + fun handleOnAdd(edge: EdgeType) { + onAdd?.invoke(edge) } /** * This function will be executed after an edge was removed from the container. This can be used * to unregister additional handlers, e.g. a [TypeObserver]. */ - fun handlePostRemove(edge: EdgeType) { - postRemove?.invoke(edge) + fun handleOnRemove(edge: EdgeType) { + onRemove?.invoke(edge) } } /** A helper function for [EdgeCollection.toNodeCollection]. */ -@Suppress("UNCHECKED_CAST") internal fun < NodeType : Node, + EdgeType : Edge, CollectionType : MutableCollection > internalToNodeCollection( - edges: EdgeCollection>, + edges: EdgeCollection, outgoing: Boolean = true, + predicate: ((EdgeType) -> Boolean)? = null, createCollection: () -> CollectionType ): CollectionType { - var unwrapped = createCollection() - edges.mapTo(unwrapped) { if (outgoing) it.end else it.start as NodeType } + val unwrapped = createCollection() + for (edge in edges) { + if (predicate != null && !predicate(edge)) { + continue + } + + @Suppress("UNCHECKED_CAST") + unwrapped += if (outgoing) edge.end else edge.start as NodeType + } + return unwrapped } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeList.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeList.kt index d7aad0896b..d08c95458c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeList.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeList.kt @@ -34,62 +34,59 @@ abstract class EdgeList>( override var thisRef: Node, override var init: (start: Node, end: NodeType) -> EdgeType, override var outgoing: Boolean = true, - override var postAdd: ((EdgeType) -> Unit)? = null, - override var postRemove: ((EdgeType) -> Unit)? = null + override var onAdd: ((EdgeType) -> Unit)? = null, + override var onRemove: ((EdgeType) -> Unit)? = null ) : ArrayList(), EdgeCollection { - override fun add(e: EdgeType): Boolean { + override fun add(element: EdgeType): Boolean { // Make sure, the index is always set - if (e.index == null) { - e.index = this.size + if (element.index == null) { + element.index = this.size } - val ok = super.add(e) + val ok = super.add(element) if (ok) { - handlePostAdd(e) + handleOnAdd(element) } return ok } - override fun remove(o: EdgeType): Boolean { - val ok = super.remove(o) + override fun remove(element: EdgeType): Boolean { + val ok = super.remove(element) if (ok) { - handlePostRemove(o) + handleOnRemove(element) } return ok } - override fun removeAll(c: Collection): Boolean { - val ok = super.removeAll(c) + override fun removeAll(elements: Collection): Boolean { + val ok = super.removeAll(elements.toSet()) if (ok) { - c.forEach { handlePostRemove(it) } + elements.forEach { handleOnRemove(it) } } return ok } - override fun removeRange(fromIndex: Int, toIndex: Int) { - super.removeRange(fromIndex, toIndex) - } - override fun removeAt(index: Int): EdgeType { - var edge = super.removeAt(index) - handlePostRemove(edge) + val edge = super.removeAt(index) + handleOnRemove(edge) return edge } override fun removeIf(predicate: Predicate): Boolean { - var edges = filter { predicate.test(it) } + val edges = filter { predicate.test(it) } val ok = super.removeIf(predicate) if (ok) { - edges.forEach { handlePostRemove(it) } + edges.forEach { handleOnRemove(it) } } return ok } override fun clear() { - var edges = this.toList() + // Make a copy of our edges so we can pass a copy to our on-remove handler + val edges = this.toList() super.clear() - edges.forEach { handlePostRemove(it) } + edges.forEach { handleOnRemove(it) } } override fun add(index: Int, element: EdgeType) { @@ -98,7 +95,7 @@ abstract class EdgeList>( super.add(index, element) - handlePostAdd(element) + handleOnAdd(element) // We need to re-compute all edges with an index > inserted index for (i in index until this.size) { @@ -106,8 +103,8 @@ abstract class EdgeList>( } } - override fun toNodeCollection(outgoing: Boolean): List { - return internalToNodeCollection(this, outgoing, ::ArrayList) + override fun toNodeCollection(predicate: ((EdgeType) -> Boolean)?): List { + return internalToNodeCollection(this, outgoing, predicate, ::ArrayList) } /** @@ -118,13 +115,13 @@ abstract class EdgeList>( return UnwrappedEdgeList(this) } - override fun equals(o: Any?): Boolean { - if (o !is EdgeList<*, *>) return false + override fun equals(other: Any?): Boolean { + if (other !is EdgeList<*, *>) return false // Otherwise, try to compare the contents of the lists with the propertyEquals method - if (this.size == o.size) { + if (this.size == other.size) { for (i in this.indices) { - if (this[i] != o[i]) { + if (this[i] != other[i]) { return false } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSet.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSet.kt index b5339767de..d1ca7cb372 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSet.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSet.kt @@ -37,51 +37,51 @@ abstract class EdgeSet>( override var thisRef: Node, override var init: (start: Node, end: NodeType) -> EdgeType, override var outgoing: Boolean = true, - override var postAdd: ((EdgeType) -> Unit)? = null, - override var postRemove: ((EdgeType) -> Unit)? = null + override var onAdd: ((EdgeType) -> Unit)? = null, + override var onRemove: ((EdgeType) -> Unit)? = null ) : HashSet(), EdgeCollection { - override fun add(e: EdgeType): Boolean { - val ok = super.add(e) + override fun add(element: EdgeType): Boolean { + val ok = super.add(element) if (ok) { - handlePostAdd(e) + handleOnAdd(element) } return ok } override fun removeIf(predicate: Predicate): Boolean { - var edges = filter { predicate.test(it) } + val edges = filter { predicate.test(it) } val ok = super.removeIf(predicate) if (ok) { - edges.forEach { handlePostRemove(it) } + edges.forEach { handleOnRemove(it) } } return ok } - override fun removeAll(c: Collection): Boolean { - val edges = this.toSet() - val ok = super.removeAll(c) + override fun removeAll(elements: Collection): Boolean { + val ok = super.removeAll(elements.toSet()) if (ok) { - edges.forEach { handlePostRemove(it) } + elements.forEach { handleOnRemove(it) } } return ok } - override fun remove(o: EdgeType): Boolean { - val ok = super.remove(o) + override fun remove(element: EdgeType): Boolean { + val ok = super.remove(element) if (ok) { - handlePostRemove(o) + handleOnRemove(element) } return ok } override fun clear() { - var edges = this.toList() + // Make a copy of our edges so we can pass a copy to our on-remove handler + val edges = this.toSet() super.clear() - edges.forEach { handlePostRemove(it) } + edges.forEach { handleOnRemove(it) } } - override fun toNodeCollection(outgoing: Boolean): MutableSet { - return internalToNodeCollection(this, outgoing, ::HashSet) + override fun toNodeCollection(predicate: ((EdgeType) -> Boolean)?): MutableSet { + return internalToNodeCollection(this, outgoing, predicate, ::HashSet) } /** @@ -92,12 +92,12 @@ abstract class EdgeSet>( return UnwrappedEdgeSet(this) } - override fun equals(o: Any?): Boolean { - if (this === o) return true - if (o !is EdgeSet<*, *>) return false + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is EdgeSet<*, *>) return false // Otherwise, try to compare the contents of the lists with the propertyEquals method - return this.containsAll(o) + return this.containsAll(other) } override fun hashCode(): Int { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSingletonList.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSingletonList.kt new file mode 100644 index 0000000000..2b8bab56b2 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSingletonList.kt @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edges.collections + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edges.Edge +import kotlin.reflect.KProperty +import org.neo4j.ogm.annotation.Transient + +/** + * This is a MAJOR workaround since Neo4J OGM does not allow to use our (generic) [Edge] class for + * our AST edges. See https://github.com/neo4j/neo4j-ogm/issues/1132. + * + * Therefore, we need to wrap the edge in a list with a single element. + */ +class EdgeSingletonList>( + override var thisRef: Node, + override var init: (Node, NodeType) -> EdgeType, + var onChanged: ((old: EdgeType?, new: EdgeType?) -> Unit)? = null, + override var outgoing: Boolean, + of: NullableNodeType, +) : EdgeCollection { + + var element: EdgeType? = + if (of == null) { + null + } else { + if (outgoing) { + init(thisRef, of) + } else { + @Suppress("UNCHECKED_CAST") init(of, thisRef as NodeType) + } + } + + override val size: Int + get() = if (element == null) 0 else 1 + + override fun contains(element: EdgeType): Boolean { + return this.element == element + } + + override fun containsAll(elements: Collection): Boolean { + return elements.size == 1 && this.element == elements.firstOrNull() + } + + override fun isEmpty(): Boolean { + return this.element == null + } + + override fun add(element: EdgeType): Boolean { + throw UnsupportedOperationException() + } + + override fun addAll(elements: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun clear() { + throw UnsupportedOperationException() + } + + override fun iterator(): MutableIterator { + return Iterator(element) + } + + override fun remove(element: EdgeType): Boolean { + throw UnsupportedOperationException() + } + + override fun removeAll(elements: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun retainAll(elements: Collection): Boolean { + throw UnsupportedOperationException() + } + + override var onAdd: ((EdgeType) -> Unit)? + get() = null + set(_) {} + + override var onRemove: ((EdgeType) -> Unit)? + get() = null + set(_) {} + + override fun toNodeCollection(predicate: ((EdgeType) -> Boolean)?): Collection { + val elements = predicate?.let { toList().filter(it) } ?: toList() + return elements.map { + if (outgoing) { + it.end + } else { + @Suppress("UNCHECKED_CAST") + it.start as NodeType + } + } + } + + override fun unwrap(): UnwrappedEdgeCollection { + TODO("Not yet implemented") + } + + inner class Iterator(val element: EdgeType?) : MutableIterator { + var hasNext = isNotEmpty() + + override fun remove() { + throw UnsupportedOperationException() + } + + override fun hasNext(): Boolean { + return hasNext + } + + override fun next(): EdgeType { + if (hasNext && element != null) { + hasNext = false + return element + } + throw NoSuchElementException() + } + } + + fun resetTo(node: NodeType) { + val old = this.element + this.element = + if (outgoing) { + init(thisRef, node) + } else { + @Suppress("UNCHECKED_CAST") init(node, thisRef as NodeType) + } + onChanged?.invoke(old, this.element) + } + + fun delegate(): UnwrapDelegate { + return UnwrapDelegate() + } + + @Transient + inner class UnwrapDelegate< + ThisType : Node, + >() { + @Suppress("UNCHECKED_CAST") + operator fun getValue(thisRef: ThisType, property: KProperty<*>): NullableNodeType { + return (if (outgoing) { + this@EdgeSingletonList.element?.end + } else { + this@EdgeSingletonList.element?.start as NodeType + }) + as NullableNodeType + } + + operator fun setValue(thisRef: ThisType, property: KProperty<*>, value: NullableNodeType) { + if (value != null) { + this@EdgeSingletonList.resetTo(value) + } + } + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/MirroredEdgeCollection.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/MirroredEdgeCollection.kt index 71f1008110..e817aa7bab 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/MirroredEdgeCollection.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/MirroredEdgeCollection.kt @@ -37,7 +37,7 @@ interface MirroredEdgeCollection { var mirrorProperty: KProperty> - override fun handlePostRemove(edge: PropertyEdgeType) { + override fun handleOnRemove(edge: PropertyEdgeType) { // Handle our mirror property. if (outgoing) { var prevOfNext = mirrorProperty.call(edge.end) @@ -52,14 +52,14 @@ interface MirroredEdgeCollection>( return ListIterator(list.listIterator(index)) } - @Suppress("UNCHECKED_CAST") override fun removeAt(index: Int): NodeType { var edge = list.removeAt(index) return if (list.outgoing) { edge.end } else { + @Suppress("UNCHECKED_CAST") edge.start as NodeType } } @@ -77,6 +78,7 @@ class UnwrappedEdgeList>( return if (list.outgoing) { edge.end } else { + @Suppress("UNCHECKED_CAST") edge.start as NodeType } } @@ -121,6 +123,7 @@ class UnwrappedEdgeList>( return if (list.outgoing) { next.end } else { + @Suppress("UNCHECKED_CAST") next.start as NodeType } } @@ -142,6 +145,7 @@ class UnwrappedEdgeList>( return if (list.outgoing) { next.end } else { + @Suppress("UNCHECKED_CAST") next.start as NodeType } } @@ -172,7 +176,6 @@ class UnwrappedEdgeList>( * ```kotlin * class MyNode { * @Relationship(value = "EXPRESSIONS", direction = "OUTGOING") - * @AST * var expressionsEdges = astChildrenOf() * var expressions by unwrapping(MyNode::expressionsEdges) * } 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 c08be38cc9..206e554d31 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 @@ -163,7 +163,7 @@ class Dataflows( granularity: Granularity = default(), callingContext: CallingContext ) { - var edge = + val edge = if (outgoing) { ContextSensitiveDataflow(thisRef, node, granularity, callingContext) } else { @@ -177,10 +177,10 @@ class Dataflows( * This connects our dataflow to our "mirror" property. Meaning that if we add a node to * nextDFG, we add our thisRef to the "prev" of "next" and vice-versa. */ - override fun handlePostAdd(edge: Dataflow) { - super.handlePostAdd(edge) - var start = edge.start - var thisRef = this.thisRef + override fun handleOnAdd(edge: Dataflow) { + super.handleOnAdd(edge) + val start = edge.start + val thisRef = this.thisRef // For references, we want to propagate assigned types all through the previous DFG nodes. // Therefore, we add a type observer to the previous node (if it is not ourselves) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Invoke.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Invoke.kt index e212b43593..261b4957e4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Invoke.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Invoke.kt @@ -59,7 +59,7 @@ class Invoke( /** A container for [Usage] edges. [NodeType] is necessary because of the Neo4J OGM. */ class Invokes(thisRef: CallExpression) : EdgeList(thisRef = thisRef, init = ::Invoke) { - override fun handlePostAdd(edge: Invoke) { + override fun handleOnAdd(edge: Invoke) { // TODO: Make thisRef generic :( edge.end.registerTypeObserver(thisRef as CallExpression) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ProgramDependence.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ProgramDependence.kt index 8069fb601e..30a4d09db6 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ProgramDependence.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ProgramDependence.kt @@ -71,7 +71,7 @@ class ProgramDependences : override fun add(e: Edge): Boolean { // Clone the edge before inserting. See comment above for a detailed explanation. - var clonedEdge = e.clone() + val clonedEdge = e.clone() return super.add(clonedEdge) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Usage.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Usage.kt index 8c5090d2e0..f5c7a863d7 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Usage.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/Usage.kt @@ -57,7 +57,7 @@ class Usage( /** A container for [Usage] edges. [NodeType] is necessary because of the Neo4J OGM. */ class Usages(thisRef: ValueDeclaration) : EdgeList(thisRef = thisRef, init = ::Usage) { - override fun handlePostAdd(edge: Usage) { + override fun handleOnAdd(edge: Usage) { edge.access = edge.end.access } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/AssertStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/AssertStatement.kt index a3d2433bcd..bc9910b429 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/AssertStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/AssertStatement.kt @@ -25,17 +25,21 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.Objects +import org.neo4j.ogm.annotation.Relationship /** Represents an assert statement */ class AssertStatement : Statement() { + @Relationship(value = "CONDITION") var conditionEdge = astOptionalEdgeOf() /** The condition to be evaluated. */ - @AST var condition: Expression? = null + var condition by unwrapping(AssertStatement::conditionEdge) - /** The _optional_ message that is shown, if the assert is evaluated as true */ - @AST var message: Statement? = null + @Relationship(value = "MESSAGE") var messageEdge = astOptionalEdgeOf() + /** The *optional* message that is shown, if the assert is evaluated as true */ + var message by unwrapping(AssertStatement::messageEdge) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CaseStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CaseStatement.kt index 9be9b79c0e..e0dfe6f36f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CaseStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CaseStatement.kt @@ -25,9 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.Objects +import org.neo4j.ogm.annotation.Relationship /** * Case statement of the form `case expression :` that serves as entry point for switch statements, @@ -36,11 +38,14 @@ import java.util.Objects * compound statement. */ class CaseStatement : Statement() { + @Relationship(value = "CASE_EXPRESSION") + var caseExpressionEdge = astOptionalEdgeOf() + /** * Primitive side effect free statement that has to match with the evaluated selector in * SwitchStatement */ - @AST var caseExpression: Expression? = null + var caseExpression by unwrapping(CaseStatement::caseExpressionEdge) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CatchClause.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CatchClause.kt index 8566fce554..5f317eff4c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CatchClause.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CatchClause.kt @@ -25,18 +25,23 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.BranchingNode import de.fraunhofer.aisec.cpg.graph.EOGStarterHolder import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import java.util.Objects +import org.neo4j.ogm.annotation.Relationship class CatchClause : Statement(), BranchingNode, EOGStarterHolder { - @AST var parameter: VariableDeclaration? = null + @Relationship(value = "PARAMETER") var parameterEdge = astOptionalEdgeOf() - @AST var body: Block? = null + var parameter by unwrapping(CatchClause::parameterEdge) + + @Relationship(value = "BODY") var bodyEdge = astOptionalEdgeOf() + var body by unwrapping(CatchClause::bodyEdge) override val branchedBy: Node? get() = parameter diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt index 4bdfc14ace..31285ccc59 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DeclarationStatement.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf @@ -46,7 +45,6 @@ open class DeclarationStatement : Statement() { * it only contains a single [Declaration]. */ @Relationship(value = "DECLARATIONS", direction = Relationship.Direction.OUTGOING) - @AST var declarationEdges = astEdgesOf() override var declarations by unwrapping(DeclarationStatement::declarationEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DoStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DoStatement.kt index 8d2d9b8a30..7fcafec4d4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DoStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DoStatement.kt @@ -25,24 +25,30 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.ArgumentHolder +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** Represents a conditional loop statement of the form: `do{...}while(...)`. */ class DoStatement : Statement(), ArgumentHolder { + @Relationship("CONDITION") var conditionEdge = astOptionalEdgeOf() /** * The loop condition that is evaluated after the loop statement and may trigger reevaluation. */ - @AST var condition: Expression? = null + var condition by unwrapping(DoStatement::conditionEdge) + + @Relationship("STATEMENT") var statementEdge = astOptionalEdgeOf() /** * The statement that is going to be executed and re-executed, until the condition evaluates to * false for the first time. Usually a [Block]. */ - @AST var statement: Statement? = null + var statement by unwrapping(DoStatement::statementEdge) override fun toString() = ToStringBuilder(this, TO_STRING_STYLE) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt index 28c09127d6..be4808ac26 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForEachStatement.kt @@ -29,30 +29,38 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdges import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import java.util.Objects +import org.neo4j.ogm.annotation.Relationship class ForEachStatement : Statement(), BranchingNode, StatementHolder { + + @Relationship("VARIABLE") + var variableEdge = + astOptionalEdgeOf( + onChanged = { _, new -> + val end = new?.end + if (end is Reference) { + end.access = AccessValues.WRITE + } + } + ) + /** * This field contains the iteration variable of the loop. It can be either a new variable * declaration or a reference to an existing variable. */ - @AST - var variable: Statement? = null - set(value) { - if (value is Reference) { - value.access = AccessValues.WRITE - } - field = value - } + var variable by unwrapping(ForEachStatement::variableEdge) + @Relationship("ITERABLE") var iterableEdge = astOptionalEdgeOf() /** This field contains the iteration subject of the loop. */ - @AST var iterable: Statement? = null + var iterable by unwrapping(ForEachStatement::iterableEdge) + @Relationship("STATEMENT") var statementEdge = astOptionalEdgeOf() /** This field contains the body of the loop. */ - @AST var statement: Statement? = null + var statement by unwrapping(ForEachStatement::statementEdge) override val branchedBy: Node? get() = iterable @@ -71,22 +79,6 @@ class ForEachStatement : Statement(), BranchingNode, StatementHolder { override var statements by unwrapping(ForEachStatement::statementEdges) - override fun addStatement(s: Statement) { - when { - variable == null -> variable = s - iterable == null -> iterable = s - statement == null -> statement = s - statement !is Block -> { - val block = Block() - block.language = this.language - statement?.let { block.statements += it } - block.statements += s - statement = block - } - else -> (statement as? Block)?.statements += s - } - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is ForEachStatement) return false diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForStatement.kt index 7bc4dff12e..3e36632d7b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ForStatement.kt @@ -27,19 +27,29 @@ package de.fraunhofer.aisec.cpg.graph.statements import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.* +import org.neo4j.ogm.annotation.Relationship class ForStatement : Statement(), BranchingNode { - @AST var statement: Statement? = null + @Relationship("STATEMENT") var statementEdge = astOptionalEdgeOf() + var statement by unwrapping(ForStatement::statementEdge) - @AST var initializerStatement: Statement? = null + @Relationship("INITIALIZER_STATEMENT") + var initializerStatementEdge = astOptionalEdgeOf() + var initializerStatement by unwrapping(ForStatement::initializerStatementEdge) - @AST var conditionDeclaration: Declaration? = null + @Relationship("CONDITION_DECLARATION") + var conditionDeclarationEdge = astOptionalEdgeOf() + var conditionDeclaration by unwrapping(ForStatement::conditionDeclarationEdge) - @AST var condition: Expression? = null + @Relationship("CONDITION") var conditionEdge = astOptionalEdgeOf() + var condition by unwrapping(ForStatement::conditionEdge) - @AST var iterationStatement: Statement? = null + @Relationship("ITERATION_STATEMENT") var iterationStatementEdge = astOptionalEdgeOf() + var iterationStatement by unwrapping(ForStatement::iterationStatementEdge) override val branchedBy: Node? get() = condition ?: conditionDeclaration diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/IfStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/IfStatement.kt index 4321309894..346d78b533 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/IfStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/IfStatement.kt @@ -25,25 +25,33 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.ArgumentHolder import de.fraunhofer.aisec.cpg.graph.BranchingNode import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** Represents a condition control flow statement, usually indicating by `If`. */ class IfStatement : Statement(), BranchingNode, ArgumentHolder { + @Relationship(value = "INITIALIZER_STATEMENT") + var initializerStatementEdge = astOptionalEdgeOf() /** C++ initializer statement. */ - @AST var initializerStatement: Statement? = null + var initializerStatement by unwrapping(IfStatement::initializerStatementEdge) + @Relationship(value = "CONDITION_DECLARATION") + var conditionDeclarationEdge = astOptionalEdgeOf() /** C++ alternative to the condition. */ - @AST var conditionDeclaration: Declaration? = null + var conditionDeclaration by unwrapping(IfStatement::conditionDeclarationEdge) + @Relationship(value = "CONDITION") var conditionEdge = astOptionalEdgeOf() /** The condition to be evaluated. */ - @AST var condition: Expression? = null + var condition by unwrapping(IfStatement::conditionEdge) override val branchedBy: Node? get() = condition ?: conditionDeclaration @@ -51,13 +59,15 @@ class IfStatement : Statement(), BranchingNode, ArgumentHolder { /** C++ constexpr construct. */ var isConstExpression = false + @Relationship(value = "THEN_STATEMENT") var thenStatementEdge = astOptionalEdgeOf() /** The statement that is executed, if the condition is evaluated as true. Usually a [Block]. */ - @AST var thenStatement: Statement? = null + var thenStatement by unwrapping(IfStatement::thenStatementEdge) + @Relationship(value = "ELSE_STATEMENT") var elseStatementEdge = astOptionalEdgeOf() /** * The statement that is executed, if the condition is evaluated as false. Usually a [Block]. */ - @AST var elseStatement: Statement? = null + var elseStatement by unwrapping(IfStatement::elseStatementEdge) override fun toString(): String { return ToStringBuilder(this, TO_STRING_STYLE) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt index 8d3134ab22..88eddde61c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/LabelStatement.kt @@ -25,22 +25,25 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.StatementHolder import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdges import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** * A label attached to a statement that is used to change control flow by labeled continue and * breaks (Java) or goto(C++). */ class LabelStatement : Statement(), StatementHolder { + @Relationship(value = "SUB_STATEMENT") var subStatementEdge = astOptionalEdgeOf() + /** Statement that the label is attached to. Can be a simple or compound statement. */ - @AST var subStatement: Statement? = null + var subStatement by unwrapping(LabelStatement::subStatementEdge) /** Label in the form of a String */ var label: String? = null diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ReturnStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ReturnStatement.kt index 01c22dd88f..420db4def0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ReturnStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ReturnStatement.kt @@ -25,16 +25,20 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.ArgumentHolder +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** Represents a statement that returns out of the current function. */ class ReturnStatement : Statement(), ArgumentHolder { + @Relationship(value = "RETURN_VALUES") var returnValueEdges = astEdgesOf() + /** The expression whose value will be returned. */ - @AST var returnValues: MutableList = mutableListOf() + var returnValues by unwrapping(ReturnStatement::returnValueEdges) /** * A utility property to handle single-valued return statements. In case [returnValues] contains diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt index cc3d2b2d0f..8c052546d6 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/Statement.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.DeclarationHolder import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration @@ -50,7 +49,6 @@ abstract class Statement : Node(), DeclarationHolder { * TODO: This is actually an AST node just for a subset of nodes, i.e. initializers in for-loops */ @Relationship(value = "LOCALS", direction = Relationship.Direction.OUTGOING) - @AST var localEdges = astEdgesOf() /** Virtual property to access [localEdges] without property edges. */ diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SwitchStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SwitchStatement.kt index f9e39ca7e4..e677cc8911 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SwitchStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SwitchStatement.kt @@ -25,12 +25,14 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.BranchingNode import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.Objects +import org.neo4j.ogm.annotation.Relationship /** * Represents a Java or C++ switch statement of the `switch (selector) {...}` that can include case @@ -38,20 +40,26 @@ import java.util.Objects * handled properly. */ class SwitchStatement : Statement(), BranchingNode { + @Relationship(value = "SELECTOR") var selectorEdge = astOptionalEdgeOf() /** Selector that determines the case/default statement of the subsequent execution */ - @AST var selector: Expression? = null + var selector by unwrapping(SwitchStatement::selectorEdge) + @Relationship(value = "INITIALIZER_STATEMENT") + var initializerStatementEdge = astOptionalEdgeOf() /** C++ can have an initializer statement in a switch */ - @AST var initializerStatement: Statement? = null + var initializerStatement by unwrapping(SwitchStatement::initializerStatementEdge) + @Relationship(value = "SELECTOR_DECLARATION") + var selectorDeclarationEdge = astOptionalEdgeOf() /** C++ allows to use a declaration instead of a expression as selector */ - @AST var selectorDeclaration: Declaration? = null + var selectorDeclaration by unwrapping(SwitchStatement::selectorDeclarationEdge) + @Relationship(value = "STATEMENT") var statementEdge = astOptionalEdgeOf() /** * The compound statement that contains break/default statements with regular statements on the * same hierarchy */ - @AST var statement: Statement? = null + var statement by unwrapping(SwitchStatement::statementEdge) override val branchedBy: Node? get() = selector diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SynchronizedStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SynchronizedStatement.kt index c6ed76a482..ac955b24ce 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SynchronizedStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/SynchronizedStatement.kt @@ -25,15 +25,19 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.Objects +import org.neo4j.ogm.annotation.Relationship class SynchronizedStatement : Statement() { - @AST var expression: Expression? = null + @Relationship(value = "EXPRESSION") var expressionEdge = astOptionalEdgeOf() + var expression by unwrapping(SynchronizedStatement::expressionEdge) - @AST var block: Block? = null + @Relationship(value = "BLOCK") var blockEdge = astOptionalEdgeOf() + var block by unwrapping(SynchronizedStatement::blockEdge) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt index 052ed72346..86df0074e7 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/TryStatement.kt @@ -25,9 +25,9 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import java.util.* @@ -36,16 +36,16 @@ import org.neo4j.ogm.annotation.Relationship /** A [Statement] which represents a try/catch block, primarily used for exception handling. */ class TryStatement : Statement() { @Relationship(value = "RESOURCES", direction = Relationship.Direction.OUTGOING) - @AST var resourceEdges = astEdgesOf() var resources by unwrapping(TryStatement::resourceEdges) - @AST var tryBlock: Block? = null + @Relationship(value = "TRY_BLOCK") var tryBlockEdge = astOptionalEdgeOf() + var tryBlock by unwrapping(TryStatement::tryBlockEdge) - @AST var finallyBlock: Block? = null + @Relationship(value = "FINALLY_BLOCK") var finallyBlockEdge = astOptionalEdgeOf() + var finallyBlock by unwrapping(TryStatement::finallyBlockEdge) @Relationship(value = "CATCH_CLAUSES", direction = Relationship.Direction.OUTGOING) - @AST var catchClauseEdges = astEdgesOf() var catchClauses by unwrapping(TryStatement::catchClauseEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/WhileStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/WhileStatement.kt index 66dffba5b8..11d3f1a9cf 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/WhileStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/WhileStatement.kt @@ -25,28 +25,35 @@ */ package de.fraunhofer.aisec.cpg.graph.statements -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.ArgumentHolder import de.fraunhofer.aisec.cpg.graph.BranchingNode import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** Represents a conditional loop statement of the form: `while(...){...}`. */ class WhileStatement : Statement(), BranchingNode, ArgumentHolder { + @Relationship(value = "CONDITION_DECLARATION") + var conditionDeclarationEdge = astOptionalEdgeOf() /** C++ allows defining a declaration instead of a pure logical expression as condition */ - @AST var conditionDeclaration: Declaration? = null + var conditionDeclaration by unwrapping(WhileStatement::conditionDeclarationEdge) + @Relationship(value = "CONDITION") var conditionEdge = astOptionalEdgeOf() /** The condition that decides if the block is executed. */ - @AST var condition: Expression? = null + var condition by unwrapping(WhileStatement::conditionEdge) + @Relationship(value = "STATEMENT") var statementEdge = astOptionalEdgeOf() /** * The statement that is going to be executed, until the condition evaluates to false for the * first time. Usually a [Block]. */ - @AST var statement: Statement? = null + var statement by unwrapping(WhileStatement::statementEdge) override val branchedBy: Node? get() = condition ?: conditionDeclaration diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt index 6787d4c7fd..966a913545 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt @@ -28,9 +28,12 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.TupleType import de.fraunhofer.aisec.cpg.graph.types.Type +import org.neo4j.ogm.annotation.Relationship import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -56,31 +59,36 @@ class AssignExpression : override var operatorCode: String = "=" - @AST - var lhs: List = listOf() - set(value) { - field = value - field.forEach { - var base = (it as? MemberExpression)?.base as? MemberExpression + /** The expressions on the left-hand side. */ + @Relationship("LHS") + var lhsEdges = + astEdgesOf( + onAdd = { + var end = it.end + var base = (end as? MemberExpression)?.base as? MemberExpression while (base != null) { base.access = AccessValues.READWRITE - base = (base as? MemberExpression)?.base as? MemberExpression + base = base.base as? MemberExpression + } + + if (isSimpleAssignment) { + (end as? Reference)?.access = AccessValues.WRITE + } else { + (end as? Reference)?.access = AccessValues.READWRITE } } - if (isSimpleAssignment) { - field.forEach { (it as? Reference)?.access = AccessValues.WRITE } - } else { - field.forEach { (it as? Reference)?.access = AccessValues.READWRITE } - } - } + ) + var lhs by unwrapping(AssignExpression::lhsEdges) - @AST - var rhs: List = listOf() - set(value) { - field.forEach { it.unregisterTypeObserver(this) } - field = value - value.forEach { it.registerTypeObserver(this) } - } + @Relationship("RHS") + + /** The expressions on the right-hand side. */ + var rhsEdges = + astEdgesOf( + onAdd = { it.end.registerTypeObserver(this) }, + onRemove = { it.end.unregisterTypeObserver(this) }, + ) + var rhs by unwrapping(AssignExpression::rhsEdges) /** * This property specifies, that this is actually used as an expression. Not many languages @@ -117,6 +125,7 @@ class AssignExpression : return operatorCode in (language?.simpleAssignmentOperators ?: setOf()) } + @Relationship("DECLARATIONS") var declarationEdges = astEdgesOf() /** * Some languages, such as Go explicitly allow the definition / declaration of variables in the * assignment (known as a "short assignment"). Some languages, such as Python even implicitly @@ -125,7 +134,7 @@ class AssignExpression : * we need to later resolve this in an additional pass. The declarations are then stored in * [declarations]. */ - @AST override var declarations = mutableListOf() + override var declarations by unwrapping(AssignExpression::declarationEdges) /** Finds the value (of [rhs]) that is assigned to the particular [lhs] expression. */ fun findValue(lhsExpression: HasType): Expression? { @@ -223,18 +232,18 @@ class AssignExpression : override fun addArgument(expression: Expression) { if (lhs.isEmpty()) { - lhs = listOf(expression) + lhs = mutableListOf(expression) } else { - rhs = listOf(expression) + rhs = mutableListOf(expression) } } override fun replaceArgument(old: Expression, new: Expression): Boolean { - return if (lhs == listOf(old)) { - lhs = listOf(new) + return if (lhs.singleOrNull() == old) { + lhs = mutableListOf(new) true - } else if (rhs == listOf(old)) { - rhs = listOf(new) + } else if (rhs.singleOrNull() == old) { + rhs = mutableListOf(new) true } else { false diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt index f37f34be63..560326b100 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt @@ -27,10 +27,13 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** * A binary operation expression, such as "a + b". It consists of a left hand expression (lhs), a @@ -40,23 +43,24 @@ import org.apache.commons.lang3.builder.ToStringBuilder */ open class BinaryOperator : Expression(), HasOverloadedOperation, ArgumentHolder, HasType.TypeObserver { + /** The left-hand expression. */ - @AST - var lhs: Expression = ProblemExpression("could not parse lhs") - set(value) { - disconnectOldLhs() - field = value - connectNewLhs(value) - } + @Relationship("LHS") + var lhsEdge = + astEdgeOf( + of = ProblemExpression("could not parse lhs"), + onChanged = ::exchangeTypeObserver + ) + var lhs by unwrapping(BinaryOperator::lhsEdge) /** The right-hand expression. */ - @AST - var rhs: Expression = ProblemExpression("could not parse rhs") - set(value) { - disconnectOldRhs() - field = value - connectNewRhs(value) - } + @Relationship("RHS") + var rhsEdge = + astEdgeOf( + of = ProblemExpression("could not parse rhs"), + onChanged = ::exchangeTypeObserver + ) + var rhs by unwrapping(BinaryOperator::rhsEdge) /** The operator code. */ override var operatorCode: String? = null @@ -72,31 +76,6 @@ open class BinaryOperator : } } - private fun connectNewLhs(lhs: Expression) { - lhs.registerTypeObserver(this) - if (lhs is Reference && "=" == operatorCode) { - // declared reference expr is the left-hand side of an assignment -> writing to the var - lhs.access = AccessValues.WRITE - } else if ( - lhs is Reference && operatorCode in (language?.compoundAssignmentOperators ?: setOf()) - ) { - // declared reference expr is the left-hand side of an assignment -> writing to the var - lhs.access = AccessValues.READWRITE - } - } - - private fun disconnectOldLhs() { - lhs.unregisterTypeObserver(this) - } - - private fun connectNewRhs(rhs: Expression) { - rhs.registerTypeObserver(this) - } - - private fun disconnectOldRhs() { - rhs.unregisterTypeObserver(this) - } - override fun toString(): String { return ToStringBuilder(this, TO_STRING_STYLE) .append("lhs", lhs.name) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt index 8f77c67674..a8188e8fb4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt @@ -25,8 +25,8 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.StatementHolder +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.edges.Edge import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping @@ -42,7 +42,6 @@ import org.neo4j.ogm.annotation.Relationship open class Block : Expression(), StatementHolder { /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) - @AST override var statementEdges = astEdgesOf() override var statements by unwrapping(Block::statementEdges) /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt index 1e24a4ba85..4530598aab 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -33,6 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.edges.* import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge import de.fraunhofer.aisec.cpg.graph.edges.ast.TemplateArguments +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.flows.Invokes import de.fraunhofer.aisec.cpg.graph.types.* @@ -64,7 +65,6 @@ open class CallExpression : /** The list of arguments of this call expression, backed by a list of [Edge] objects. */ @Relationship(value = "ARGUMENTS", direction = Relationship.Direction.OUTGOING) - @AST var argumentEdges = astEdgesOf() /** @@ -84,7 +84,10 @@ open class CallExpression : * but will be in the future. In most cases, this is a [Reference] and its [Reference.refersTo] * is intentionally left empty. It is not filled by the [SymbolResolver]. */ - @AST var callee: Expression = ProblemExpression("could not parse callee") + @Relationship(value = "CALLEE", direction = Relationship.Direction.OUTGOING) + private var calleeEdge = astEdgeOf(ProblemExpression("could not parse callee")) + + var callee by unwrapping(CallExpression::calleeEdge) /** * The [Name] of this call expression, based on its [callee]. @@ -148,7 +151,6 @@ open class CallExpression : /** If the CallExpression instantiates a template, the call can provide template arguments. */ @Relationship(value = "TEMPLATE_ARGUMENTS", direction = Relationship.Direction.OUTGOING) - @AST var templateArgumentEdges: TemplateArguments? = null set(value) { field = value diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt index d011534dbe..541cf38a25 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt @@ -26,19 +26,31 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* +import org.neo4j.ogm.annotation.Relationship import org.slf4j.LoggerFactory class CastExpression : Expression(), ArgumentHolder, HasType.TypeObserver { - @AST - var expression: Expression = ProblemExpression("could not parse inner expression") - set(value) { - field.unregisterTypeObserver(this) - field = value - value.registerTypeObserver(this) - } + /** + * The [Expression] that is cast to [castType]. + * + * Note: While the [type] will always stay the same (i.e. the [castType]), we still want to + * register ourselves as a type observer to the expression. The reason for that is that we want + * to propagate the [assignedTypes] of our [expression] to us and then possibly to other nodes. + * This way we can still access the original type of expression (e.g., created by a + * [NewExpression]), even when it is cast. + */ + @Relationship(type = "EXPRESSION") + var expressionEdge = + astEdgeOf( + of = ProblemExpression("could not parse inner expression"), + onChanged = ::exchangeTypeObserver + ) + var expression by unwrapping(CastExpression::expressionEdge) var castType: Type = unknownType() set(value) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt index c18a001449..977c85d8ed 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt @@ -27,33 +27,44 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.commonType import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** * Represents an expression containing a ternary operator: `var x = condition ? valueIfTrue : * valueIfFalse`; */ class ConditionalExpression : Expression(), ArgumentHolder, BranchingNode, HasType.TypeObserver { - @AST var condition: Expression = ProblemExpression("could not parse condition expression") + @Relationship("CONDITION") + var conditionEdge = + astEdgeOf(ProblemExpression("could not parse condition expression")) + var condition by unwrapping(ConditionalExpression::conditionEdge) - @AST - var thenExpression: Expression? = null - set(value) { - field?.unregisterTypeObserver(this) - field = value - value?.registerTypeObserver(this) - } + @Relationship("THEN_EXPRESSION") + var thenExpressionEdge = + astOptionalEdgeOf( + onChanged = { old, new -> + old?.end?.unregisterTypeObserver(this) + new?.end?.registerTypeObserver(this) + } + ) + var thenExpression by unwrapping(ConditionalExpression::thenExpressionEdge) - @AST - var elseExpression: Expression? = null - set(value) { - field?.unregisterTypeObserver(this) - field = value - value?.registerTypeObserver(this) - } + @Relationship("ELSE_EXPRESSION") + var elseExpressionEdge = + astOptionalEdgeOf( + onChanged = { old, new -> + old?.end?.unregisterTypeObserver(this) + new?.end?.registerTypeObserver(this) + } + ) + var elseExpression by unwrapping(ConditionalExpression::elseExpressionEdge) override fun toString(): String { return ToStringBuilder(this, TO_STRING_STYLE) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt index 86ac7320a1..c6c81d51ae 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt @@ -28,10 +28,13 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.PopulatedByPass import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.passes.SymbolResolver import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** * Represents a call to a constructor, usually as an initializer. @@ -62,7 +65,9 @@ class ConstructExpression : CallExpression() { } } - @AST var anonymousClass: RecordDeclaration? = null + @Relationship("ANONYMOUS_CLASS") var anonymousClassEdge = astOptionalEdgeOf() + + var anonymousClass by unwrapping(ConstructExpression::anonymousClassEdge) /** The [Declaration] of the type this expression instantiates. */ @PopulatedByPass(SymbolResolver::class) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeleteExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeleteExpression.kt index 590629b156..7540cf5772 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeleteExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeleteExpression.kt @@ -25,11 +25,14 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import java.util.Objects +import org.neo4j.ogm.annotation.Relationship class DeleteExpression : Expression() { - @AST var operand: Expression? = null + @Relationship("OPERAND") var operandEdge = astOptionalEdgeOf() + var operand by unwrapping(DeleteExpression::operandEdge) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt index ba5da4d3e8..f349cc7a33 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping @@ -35,7 +34,6 @@ import org.neo4j.ogm.annotation.Relationship class ExpressionList : Expression() { @Relationship(value = "SUBEXPR", direction = Relationship.Direction.OUTGOING) - @AST var expressionEdges = astEdgesOf() var expressions by unwrapping(ExpressionList::expressionEdges) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt index c8545d5bc9..62fa90ca8e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt @@ -46,11 +46,10 @@ import org.neo4j.ogm.annotation.Relationship class InitializerListExpression : Expression(), ArgumentHolder, HasType.TypeObserver { /** The list of initializers. */ @Relationship(value = "INITIALIZERS", direction = Relationship.Direction.OUTGOING) - @AST var initializerEdges = astEdgesOf( - postAdd = { it.end.registerTypeObserver(this) }, - postRemove = { it.end.unregisterTypeObserver(this) }, + onAdd = { it.end.registerTypeObserver(this) }, + onRemove = { it.end.unregisterTypeObserver(this) }, ) /** Virtual property to access [initializerEdges] without property edges. */ diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt index e25814d523..d6b2eaff81 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt @@ -25,9 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.ArgumentHolder +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import java.util.* +import org.neo4j.ogm.annotation.Relationship /** * Represents a key / value pair, often found in languages that allow associative arrays or objects, @@ -38,19 +40,22 @@ import java.util.* */ class KeyValueExpression : Expression(), ArgumentHolder { + @Relationship("KEY") var keyEdge = astEdgeOf(ProblemExpression("missing key")) /** * The key of this pair. It is usually a literal, but some languages even allow references to * variables as a key. */ - @AST var key: Expression? = null + var key by unwrapping(KeyValueExpression::keyEdge) + + @Relationship("VALUE") var valueEdge = astEdgeOf(ProblemExpression("missing value")) /** The value of this pair. It can be any expression */ - @AST var value: Expression? = null + var value by unwrapping(KeyValueExpression::valueEdge) override fun addArgument(expression: Expression) { - if (key == null) { + if (key is ProblemExpression) { key = expression - } else if (value == null) { + } else if (value is ProblemExpression) { value = expression } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt index eaff56e9d6..2b8823be97 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt @@ -28,9 +28,12 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.FunctionType import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type +import org.neo4j.ogm.annotation.Relationship /** * This expression denotes the usage of an anonymous / lambda function. It connects the inner @@ -47,13 +50,9 @@ class LambdaExpression : Expression(), HasType.TypeObserver { /** Determines if we can modify variables declared outside the lambda from inside the lambda */ var areVariablesMutable: Boolean = true - @AST - var function: FunctionDeclaration? = null - set(value) { - value?.unregisterTypeObserver(this) - field = value - value?.registerTypeObserver(this) - } + @Relationship("FUNCTION") + var functionEdge = astOptionalEdgeOf(onChanged = ::exchangeTypeObserver) + var function by unwrapping(LambdaExpression::functionEdge) override fun typeChanged(newType: Type, src: HasType) { // Make sure our src is the function diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt index d6ae489025..bd2fb0e0b2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt @@ -25,16 +25,18 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.ArgumentHolder import de.fraunhofer.aisec.cpg.graph.HasBase import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.fqn import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** * Represents access to a member of a [RecordDeclaration], such as `obj.property`. Another common @@ -42,14 +44,16 @@ import org.apache.commons.lang3.builder.ToStringBuilder * property of a [MemberCallExpression]. */ class MemberExpression : Reference(), HasOverloadedOperation, ArgumentHolder, HasBase { - @AST - override var base: Expression = ProblemExpression("could not parse base expression") - set(value) { - field.unregisterTypeObserver(this) - field = value - updateName() - value.registerTypeObserver(this) - } + @Relationship("BASE") + var baseEdge = + astEdgeOf( + ProblemExpression("could not parse base expression"), + onChanged = { old, new -> + exchangeTypeObserver(old, new) + updateName() + } + ) + override var base by unwrapping(MemberExpression::baseEdge) override var operatorCode: String? = null diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt index d6b71b0350..d0bfa683f2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewArrayExpression.kt @@ -25,11 +25,12 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.edges.Edge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping -import java.util.* +import java.util.Objects import org.neo4j.ogm.annotation.Relationship /** @@ -38,15 +39,13 @@ import org.neo4j.ogm.annotation.Relationship */ // TODO Merge and/or refactor with new Expression class NewArrayExpression : Expression() { + @Relationship("INITIALIZER") var initializerEdge = astOptionalEdgeOf() + /** * The initializer of the expression, if present. Many languages, such as Java, either specify * [dimensions] or an initializer. */ - @AST - var initializer: Expression? = null - set(value) { - field = value - } + var initializer by unwrapping(NewArrayExpression::initializerEdge) /** * Specifies the dimensions of the array that is to be created. Many languages, such as Java, @@ -54,7 +53,6 @@ class NewArrayExpression : Expression() { * dimensions. In the graph, this will NOT be done. */ @Relationship(value = "DIMENSIONS", direction = Relationship.Direction.OUTGOING) - @AST var dimensionEdges = astEdgesOf() /** Virtual property to access [dimensionEdges] without property edges. */ diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewExpression.kt index 8d6cd0c4de..807dbcdf29 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/NewExpression.kt @@ -25,25 +25,29 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasInitializer import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import java.util.Objects import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship /** Represents the creation of a new object through the `new` keyword. */ class NewExpression : Expression(), HasInitializer { + @Relationship("INITIALIZER") var initializerEdge = astOptionalEdgeOf() + /** The initializer expression. */ - @AST override var initializer: Expression? = null + override var initializer by unwrapping(NewExpression::initializerEdge) /** * We need a way to store the templateParameters that a NewExpression might have before the * ConstructExpression is created */ @Relationship(value = "TEMPLATE_PARAMETERS", direction = Relationship.Direction.OUTGOING) - @AST - var templateParameters: List? = null + var templateParameterEdges = astEdgesOf() + var templateParameters by unwrapping(NewExpression::templateParameterEdges) override fun toString(): String { return ToStringBuilder(this, TO_STRING_STYLE) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/RangeExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/RangeExpression.kt index 5a56ec7123..0140852024 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/RangeExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/RangeExpression.kt @@ -25,8 +25,10 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import java.util.* +import org.neo4j.ogm.annotation.Relationship /** * Represents the specification of a range (e.g., of an array). Usually used in combination with an @@ -53,19 +55,21 @@ import java.util.* * Individual meaning of the range indices might differ per language. */ class RangeExpression : Expression() { - + @Relationship("FLOOR") var floorEdge = astOptionalEdgeOf() /** The lower bound ("floor") of the range. This index is usually *inclusive*. */ - @AST var floor: Expression? = null + var floor by unwrapping(RangeExpression::floorEdge) + @Relationship("CEILING") var ceilingEdge = astOptionalEdgeOf() /** The upper bound ("ceiling") of the range. This index is usually *exclusive*. */ - @AST var ceiling: Expression? = null + var ceiling by unwrapping(RangeExpression::ceilingEdge) + @Relationship("THIRD") var thirdEdge = astOptionalEdgeOf() /** * Some languages offer a third value. The meaning depends completely on the language. For * example, Python allows specifying a step, while Go allows to control the underlying array's * capacity (not length). */ - @AST var third: Expression? = null + var third by unwrapping(RangeExpression::thirdEdge) /** The operator code that separates the range elements. Common cases are `:` or `...` */ var operatorCode = ":" diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/SubscriptExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/SubscriptExpression.kt index 95a272bba3..8254241a74 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/SubscriptExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/SubscriptExpression.kt @@ -26,9 +26,12 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* +import org.neo4j.ogm.annotation.Relationship /** * Represents the subscription or access of an array of the form `array[index]`, where both `array` @@ -36,22 +39,24 @@ import java.util.* * overload operators thus changing semantics of array access. */ class SubscriptExpression : Expression(), HasBase, HasType.TypeObserver, ArgumentHolder { + @Relationship("ARRAY_EXPRESSION") + var arrayExpressionEdge = + astEdgeOf( + of = ProblemExpression("could not parse array expression"), + onChanged = ::exchangeTypeObserver + ) /** The array on which the access is happening. This is most likely a [Reference]. */ - @AST - var arrayExpression: Expression = ProblemExpression("could not parse array expression") - set(value) { - field.unregisterTypeObserver(this) - field = value - type = getSubscriptType(value.type) - value.registerTypeObserver(this) - } + var arrayExpression by unwrapping(SubscriptExpression::arrayExpressionEdge) + @Relationship("SUBSCRIPT_EXPRESSION") + var subscriptExpressionEdge = + astEdgeOf(ProblemExpression("could not parse index expression")) /** * The expression which represents the "subscription" or index on which the array is accessed. * This can for example be a reference to another variable ([Reference]), a [Literal] or a * [RangeExpression]. */ - @AST var subscriptExpression: Expression = ProblemExpression("could not parse index expression") + var subscriptExpression by unwrapping(SubscriptExpression::subscriptExpressionEdge) override val base: Expression get() = arrayExpression diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt index 6e92af65dd..a5f2d88612 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt @@ -25,26 +25,30 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.AccessValues import de.fraunhofer.aisec.cpg.graph.ArgumentHolder import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation +import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.pointer import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.Type import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship /** A unary operator expression, involving one expression and an operator, such as `a++`. */ class UnaryOperator : Expression(), HasOverloadedOperation, ArgumentHolder, HasType.TypeObserver { + @Relationship("INPUT") + var inputEdge = + astEdgeOf( + of = ProblemExpression("could not parse input"), + onChanged = { old, new -> + exchangeTypeObserver(old, new) + changeExpressionAccess() + } + ) /** The expression on which the operation is applied. */ - @AST - var input: Expression = ProblemExpression("could not parse input") - set(value) { - field.unregisterTypeObserver(this) - field = value - input.registerTypeObserver(this) - changeExpressionAccess() - } + var input by unwrapping(UnaryOperator::inputEdge) /** * The unary operator does not have any arguments, since [input] is already the [operatorBase]. diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt index d50769de83..50c54c1276 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt @@ -29,6 +29,8 @@ import de.fraunhofer.aisec.cpg.graph.ContextProvider import de.fraunhofer.aisec.cpg.graph.LanguageProvider import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration +import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeSingletonList import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference @@ -127,6 +129,18 @@ interface HasType : ContextProvider, LanguageProvider { * [HasType.assignedTypes]. */ fun assignedTypeChanged(assignedTypes: Set, src: HasType) + + /** + * A helper function that can be used for [EdgeSingletonList.onChanged]. It unregisters this + * [TypeObserver] with the [old] node and registers it with the [new] one. + */ + fun exchangeTypeObserver( + old: AstEdge?, + new: AstEdge? + ) { + (old?.end as? HasType)?.unregisterTypeObserver(this) + (new?.end as? HasType)?.registerTypeObserver(this) + } } /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/CommentMatcher.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/CommentMatcher.kt index df7bfcae12..89f7ce6b66 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/CommentMatcher.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/CommentMatcher.kt @@ -49,7 +49,7 @@ class CommentMatcher { ): Node { // If there's an ArtifactLocation specified, it should at least be in the same file. val children = - SubgraphWalker.getAstChildren(node) + node.astChildren .filter { artifactLocation == null || artifactLocation == it.location?.artifactLocation } @@ -58,7 +58,7 @@ class CommentMatcher { // instead. children.addAll( children.filterIsInstance().flatMap { namespace -> - SubgraphWalker.getAstChildren(namespace).filter { it !in children } + namespace.astChildren.filter { it !in children } } ) val enclosing = @@ -95,7 +95,7 @@ class CommentMatcher { } val children = - SubgraphWalker.getAstChildren(smallestEnclosingNode) + smallestEnclosingNode.astChildren .filter { artifactLocation == null || artifactLocation == it.location?.artifactLocation } @@ -105,7 +105,7 @@ class CommentMatcher { // children with a location children.addAll( children.filterIsInstance().flatMap { namespace -> - SubgraphWalker.getAstChildren(namespace).filter { it !in children } + namespace.astChildren.filter { it !in children } } ) @@ -117,9 +117,7 @@ class CommentMatcher { children.addAll( children .filter { node -> node.location == null || node.location?.region == Region() } - .flatMap { locationLess -> - SubgraphWalker.getAstChildren(locationLess).filter { it !in children } - } + .flatMap { locationLess -> locationLess.astChildren.filter { it !in children } } ) // Searching for the closest successor to our comment amongst the children of the smallest diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt index 4659676e20..248ef670ad 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt @@ -27,17 +27,16 @@ package de.fraunhofer.aisec.cpg.helpers import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend -import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration -import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeList +import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeCollection import de.fraunhofer.aisec.cpg.processing.strategy.Strategy import java.lang.annotation.AnnotationFormatError import java.lang.reflect.Field import java.util.* import java.util.function.BiConsumer import java.util.function.Consumer -import org.neo4j.ogm.annotation.Relationship import org.slf4j.LoggerFactory /** A type for a node visitor callback for the [SubgraphWalker]. */ @@ -55,7 +54,7 @@ object SubgraphWalker { * @param classType the class type * @return its fields, including the ones from its superclass */ - private fun getAllFields(classType: Class<*>): Collection { + fun getAllEdgeFields(classType: Class<*>): Collection { if (classType.superclass != null) { val cacheKey = classType.name @@ -65,8 +64,8 @@ object SubgraphWalker { return fieldCache[cacheKey] ?: ArrayList() } val fields = ArrayList() - fields.addAll(getAllFields(classType.superclass)) - fields.addAll(listOf(*classType.declaredFields)) + fields.addAll(getAllEdgeFields(classType.superclass)) + fields.addAll(listOf(*classType.declaredFields).filter { it.name.contains("Edge") }) // update the cache fieldCache[cacheKey] = fields @@ -76,8 +75,8 @@ object SubgraphWalker { } /** - * Retrieves a list of AST children of the specified node by iterating all fields that are - * annotated with the [AST] annotation. + * Retrieves a list of AST children of the specified node by iterating all edge fields that are + * of type [AstEdge]. * * Please note, that you SHOULD NOT call this directly in a recursive function, since the AST * might have loops and you will probably run into a [StackOverflowError]. Therefore, use of @@ -92,54 +91,9 @@ object SubgraphWalker { if (node == null) return children val classType: Class<*> = node.javaClass - /*for (member in node::class.members) { - val subGraph = member.findAnnotation() - if (subGraph != null && listOf(*subGraph.value).contains("AST")) { - val old = member.isAccessible - - member.isAccessible = true - - val obj = member.call(node) - - // skip, if null - if (obj == null) { - continue - } - - member.isAccessible = old - - var outgoing = true // default - var relationship = member.findAnnotation() - if (relationship != null) { - outgoing = - relationship.direction == - Relationship.Direction.OUTGOING) - } - if (checkForPropertyEdge(field, obj)) { - obj = unwrap(obj as List>, outgoing) - } - when (obj) { - is Node -> { - children.add(obj) - } - is Collection<*> -> { - children.addAll(obj as Collection) - } - else -> { - throw AnnotationFormatError( - "Found @field:SubGraph(\"AST\") on field of type " + - obj.javaClass + - " but can only used with node graph classes or collections of graph nodes" - ) - } - } - } - }*/ - // We currently need to stick to pure Java reflection, since Kotlin reflection // is EXTREMELY slow. See https://youtrack.jetbrains.com/issue/KT-32198 - for (field in getAllFields(classType)) { - field.getAnnotation(AST::class.java) ?: continue + for (field in getAllEdgeFields(classType)) { try { // We need to synchronize access to the field, because otherwise different // threads might restore the isAccessible property while this thread is still @@ -155,28 +109,15 @@ object SubgraphWalker { obj } ?: continue - // skip, if null - var outgoing = true // default - if (field.getAnnotation(Relationship::class.java) != null) { - outgoing = - (field.getAnnotation(Relationship::class.java).direction == - Relationship.Direction.OUTGOING) - } when (obj) { - is Node -> { - children.add(obj) - } - is EdgeList<*, *> -> { - children.addAll(obj.toNodeCollection(outgoing)) - } - is Collection<*> -> { - children.addAll(obj.filterIsInstance()) + is EdgeCollection<*, *> -> { + children.addAll(obj.toNodeCollection({ it is AstEdge<*> })) } else -> { throw AnnotationFormatError( - "Found @AST on field of type " + + "Found on field of type " + obj.javaClass + - " but can only used with node graph classes or collections of graph nodes" + " but can only used with edge classes or edge collections" ) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt index 0ef968c154..a8ddc2e5d4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt @@ -436,7 +436,7 @@ object Util { * @return */ fun getAdjacentDFGNodes(n: Node?, incoming: Boolean): MutableList { - val subnodes = SubgraphWalker.getAstChildren(n) + val subnodes = n?.astChildren ?: listOf() val adjacentNodes = if (incoming) { subnodes.filter { it.nextDFG.contains(n) }.toMutableList() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EdgeCachePass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EdgeCachePass.kt deleted file mode 100644 index 3d97fca01d..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EdgeCachePass.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2021, 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.passes - -import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.graph.Component -import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker -import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn -import de.fraunhofer.aisec.cpg.processing.IVisitor -import de.fraunhofer.aisec.cpg.processing.strategy.Strategy - -enum class EdgeType { - AST, - DFG, - EOG -} - -class Edge(val source: Node, val target: Node, val type: EdgeType) - -/** The edges cache. */ -object Edges { - private val fromMap: MutableMap> = HashMap() - private val toMap: MutableMap> = HashMap() - - fun add(edge: Edge) { - if (fromMap[edge.source] == null) { - fromMap[edge.source] = ArrayList() - } - - if (toMap[edge.target] == null) { - toMap[edge.target] = ArrayList() - } - - fromMap[edge.source]?.add(edge) - toMap[edge.target]?.add(edge) - } - - fun to(node: Node, type: EdgeType): List { - return toMap.computeIfAbsent(node) { mutableListOf() }.filter { it.type == type } - } - - fun from(node: Node, type: EdgeType): List { - return fromMap.computeIfAbsent(node) { mutableListOf() }.filter { it.type == type } - } - - fun size(): Int { - return fromMap.size - } - - fun clear() { - toMap.clear() - fromMap.clear() - } -} - -/** - * This pass creates a simple cache of commonly used edges, such as DFG or AST to quickly traverse - * them in different directions. - * - * The cache itself is stored in the [Edges] object. - */ -@DependsOn(EvaluationOrderGraphPass::class) -@DependsOn(SymbolResolver::class) -@DependsOn(DFGPass::class) -@DependsOn(DynamicInvokeResolver::class) -@DependsOn(ControlFlowSensitiveDFGPass::class) -class EdgeCachePass(ctx: TranslationContext) : ComponentPass(ctx) { - override fun accept(component: Component) { - Edges.clear() - - for (tu in component.translationUnits) { - tu.accept( - Strategy::AST_FORWARD, - object : IVisitor() { - override fun visit(t: Node) { - visitAST(t) - visitDFG(t) - visitEOG(t) - - super.visit(t) - } - } - ) - } - } - - protected fun visitAST(n: Node) { - for (node in SubgraphWalker.getAstChildren(n)) { - val edge = Edge(n, node, EdgeType.AST) - Edges.add(edge) - } - } - - protected fun visitDFG(n: Node) { - for (dfg in n.prevDFG) { - val edge = Edge(dfg, n, EdgeType.DFG) - Edges.add(edge) - } - - for (dfg in n.nextDFG) { - val edge = Edge(n, dfg, EdgeType.DFG) - Edges.add(edge) - } - } - - protected fun visitEOG(n: Node) { - for (eog in n.prevEOG) { - val edge = Edge(eog, n, EdgeType.EOG) - Edges.add(edge) - } - - for (eog in n.nextEOG) { - val edge = Edge(n, eog, EdgeType.EOG) - Edges.add(edge) - } - } - - override fun cleanup() { - // nothing to do - } -} - -val Node.astParent: Node? - get() { - return Edges.to(this, EdgeType.AST).firstOrNull()?.source - } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt index f6f79d2858..ce3e2e3f21 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt @@ -329,7 +329,7 @@ class Inference internal constructor(val start: Node, override val ctx: Translat inferred.startInference(ctx)?.inferTemplateParameter(inferredTypeIdentifier) typeCounter++ if (typeParamDeclaration != null) { - inferred.addParameter(typeParamDeclaration) + inferred.parameters += typeParamDeclaration } } else if (node is Expression) { val inferredNonTypeIdentifier = "N$nonTypeCounter" @@ -342,7 +342,7 @@ class Inference internal constructor(val start: Node, override val ctx: Translat } nonTypeCounter++ if (paramVariableDeclaration != null) { - inferred.addParameter(paramVariableDeclaration) + inferred.parameters += paramVariableDeclaration } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/processing/strategy/Strategy.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/processing/strategy/Strategy.kt index e8f73db852..e3b43c97bf 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/processing/strategy/Strategy.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/processing/strategy/Strategy.kt @@ -26,7 +26,6 @@ package de.fraunhofer.aisec.cpg.processing.strategy import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker.getAstChildren import java.util.* /** Strategies (iterators) for traversing graphs to be used by visitors. */ @@ -88,6 +87,6 @@ object Strategy { * @return */ fun AST_FORWARD(x: Node): Iterator { - return getAstChildren(x).iterator() + return x.astChildren.iterator() } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt index 97c7aa0f67..605ec1fa82 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt @@ -33,7 +33,6 @@ import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.newInitializerListExpression import de.fraunhofer.aisec.cpg.graph.newVariableDeclaration import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.POINTER -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import de.fraunhofer.aisec.cpg.sarif.Region import java.net.URI @@ -1062,7 +1061,6 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerPass() .registerLanguage(TestLanguage(".")) .build() ) = @@ -1134,7 +1132,6 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerPass() .registerLanguage(TestLanguage(".")) .build() ) = diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ArgumentHolderTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ArgumentHolderTest.kt index 207ac24515..ffc2c65bb6 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ArgumentHolderTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ArgumentHolderTest.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -46,7 +47,7 @@ class ArgumentHolderTest { newConditionalExpression(newLiteral(true)), newDoStatement(), newInitializerListExpression(), - newKeyValueExpression(), + newKeyValueExpression(key = ProblemExpression(), value = ProblemExpression()), newSubscriptExpression(), newWhileStatement(), newAssignExpression(), diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt index b000e343f3..c1e929a2e1 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt @@ -33,7 +33,6 @@ import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement import de.fraunhofer.aisec.cpg.graph.statements.IfStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass import de.fraunhofer.aisec.cpg.test.* import kotlin.test.* import org.junit.jupiter.api.BeforeAll @@ -232,7 +231,6 @@ class ShortcutsTest { GraphExamples.getShortcutClass( TranslationConfiguration.builder() .defaultPasses() - .registerPass() .registerLanguage(TestLanguage(".")) .build() ) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/WalkerTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/WalkerTest.kt index 8bb6c79cfd..75c3bde692 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/WalkerTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/WalkerTest.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.graph.declarations.* +import de.fraunhofer.aisec.cpg.graph.edges.astEdges import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal @@ -107,6 +108,17 @@ class WalkerTest : BaseTest() { log.info("Flat AST has {} nodes", flat.size) } + + // Alternative approach using new edge extensions + val bench = Benchmark(WalkerTest::class.java, "Speed of Walker") + val flat = tu.astEdges.map { it.end } + bench.stop() + + assertNotNull(flat) + + assertEquals(82618, flat.size) + + log.info("Flat AST has {} nodes", flat.size) } // 741ms with branch diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/EdgeWalkerTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/EdgeWalkerTest.kt new file mode 100644 index 0000000000..33e90c0cc3 --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/EdgeWalkerTest.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. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.edges.Edge +import de.fraunhofer.aisec.cpg.graph.edges.allEdges +import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge +import de.fraunhofer.aisec.cpg.graph.edges.astEdges +import de.fraunhofer.aisec.cpg.graph.edges.edges +import de.fraunhofer.aisec.cpg.graph.newBlock +import de.fraunhofer.aisec.cpg.graph.newCallExpression +import de.fraunhofer.aisec.cpg.graph.newFunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.newReference +import kotlin.test.Test +import kotlin.test.assertEquals + +class EdgeWalkerTest { + @Test + fun testExtract() { + with(TestLanguageFrontend()) { + var node = newFunctionDeclaration("do") + node.body = newBlock() + + node.prevDFG += newCallExpression(newReference("do")) + + var edges = node.edges>() + assertEquals(2, edges.size) + + edges = node.edges { it is AstEdge } + assertEquals(1, edges.size) + + var all = node.allEdges>() + assertEquals(3, all.size) + + var allAst = node.astEdges + assertEquals(1, allAst.size) + } + } +} diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSingletonListTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSingletonListTest.kt new file mode 100644 index 0000000000..465640e25d --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/collections/EdgeSingletonListTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.edges.collections + +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf +import de.fraunhofer.aisec.cpg.graph.edges.unwrapping +import de.fraunhofer.aisec.cpg.graph.newLiteral +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression +import kotlin.test.Test +import kotlin.test.assertNull +import kotlin.test.assertSame + +class EdgeSingletonListTest { + @Test + fun testNullable() { + with(TestLanguageFrontend()) { + class MyNode : Node() { + var edge = astOptionalEdgeOf() + var unwrapped by unwrapping(MyNode::edge) + } + + var node = MyNode() + assertNull(node.unwrapped) + + node.unwrapped = newLiteral(1) + assertSame(node.unwrapped, node.edge.element?.end) + } + } +} diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ControlDependenceTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ControlDependenceTest.kt index 2cdbd72feb..55b77a35e0 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ControlDependenceTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/edges/flows/ControlDependenceTest.kt @@ -33,7 +33,7 @@ import kotlin.test.assertSame class ControlDependenceTest { @Test - fun testPostAdd() { + fun testOnAdd() { with(TestLanguageFrontend()) { // -- CDG --> // this should be 1 nextDFG for node1 and 1 prevDFG for node2 diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt index 85d24ceb4d..210aa97d68 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt @@ -314,7 +314,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : val defaultType = frontend.typeOf(templateParameter.defaultType) typeParamDecl.default = defaultType } - templateDeclaration.addParameter(typeParamDecl) + templateDeclaration.parameters += typeParamDecl } else if (templateParameter is CPPASTParameterDeclaration) { // Handle Value Parameters val nonTypeTemplateParamDeclaration = @@ -333,7 +333,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : it.nextDFGEdges += nonTypeTemplateParamDeclaration } } - templateDeclaration.addParameter(nonTypeTemplateParamDeclaration) + templateDeclaration.parameters += nonTypeTemplateParamDeclaration frontend.scopeManager.addDeclaration(nonTypeTemplateParamDeclaration) } } @@ -422,7 +422,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : handleDeclarationSpecifier(declSpecifier, ctx, sequence) // Fill template params, if needed - val templateParams: List? = extractTemplateParams(ctx, declSpecifier) + val templateParams = extractTemplateParams(ctx, declSpecifier) // Loop through all declarators, as we can potentially have multiple declarations here for (declarator in ctx.declarators) { @@ -484,7 +484,9 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : // initializer. if (declaration is VariableDeclaration) { // Set template parameters of the variable (if any) - declaration.templateParameters = templateParams + if (templateParams != null) { + declaration.templateParameters = templateParams + } // Parse the initializer, if we have one declarator.initializer?.let { 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 e9b8b566ab..48ba736625 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 @@ -261,10 +261,11 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : // we also need to "forward" our template parameters (if we have any) to the construct // expression since the construct expression will do the actual template instantiation - if (newExpression.templateParameters?.isNotEmpty() == true) { - newExpression.templateParameters?.let { - addImplicitTemplateParametersToCall(it, initializer as ConstructExpression) - } + if (newExpression.templateParameters.isNotEmpty() == true) { + addImplicitTemplateParametersToCall( + newExpression.templateParameters, + initializer as ConstructExpression + ) } // our initializer, such as a construct expression, will have the non-pointer type @@ -281,8 +282,8 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : * @param template * @return List of Nodes containing the all the arguments the template was instantiated with. */ - private fun getTemplateArguments(template: CPPASTTemplateId): List { - val templateArguments: MutableList = ArrayList() + private fun getTemplateArguments(template: CPPASTTemplateId): MutableList { + val templateArguments = mutableListOf() for (argument in template.templateArguments) { when (argument) { is IASTTypeId -> { diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt index 413b7a6b07..a1afbe40ff 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt @@ -36,7 +36,6 @@ import de.fraunhofer.aisec.cpg.graph.newLiteral import de.fraunhofer.aisec.cpg.graph.primitiveType import de.fraunhofer.aisec.cpg.graph.statements.expressions.InitializerListExpression import de.fraunhofer.aisec.cpg.helpers.Benchmark -import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.test.* import java.time.Duration import java.time.temporal.ChronoUnit @@ -105,7 +104,7 @@ class PerformanceRegressionTest { } fun doNothing(node: Node) { - for (child in SubgraphWalker.getAstChildren(node)) { + for (child in node.astChildren) { doNothing(child) } } diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index cb9b1258ca..d19f75f502 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.golang -import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.analysis.MultiValueEvaluator import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration @@ -34,7 +33,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.FunctionType import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.PointerType -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass import de.fraunhofer.aisec.cpg.test.* import java.io.File import java.nio.file.Path @@ -1207,7 +1205,6 @@ class GoLanguageFrontendTest : BaseTest() { val tu = analyzeAndGetFirstTU(listOf(topLevel.resolve("eval.go").toFile()), topLevel, true) { it.registerLanguage() - it.registerPass() } assertNotNull(tu) diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/StatementHandler.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/StatementHandler.kt index 9ee7ad31a8..c969dd3757 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/StatementHandler.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/StatementHandler.kt @@ -105,8 +105,10 @@ class StatementHandler(frontend: JVMLanguageFrontend) : private fun handleAbstractDefinitionStmt(defStmt: AbstractDefinitionStmt): AssignExpression { val assign = newAssignExpression("=", rawNode = defStmt) - assign.lhs = listOfNotNull(frontend.expressionHandler.handle(defStmt.leftOp)) - assign.rhs = listOfNotNull(frontend.expressionHandler.handle(defStmt.rightOp)) + assign.lhs = + listOfNotNull(frontend.expressionHandler.handle(defStmt.leftOp)).toMutableList() + assign.rhs = + listOfNotNull(frontend.expressionHandler.handle(defStmt.rightOp)).toMutableList() return assign } diff --git a/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontendTest.kt b/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontendTest.kt index 2577a63de9..a30901c681 100644 --- a/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontendTest.kt +++ b/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontendTest.kt @@ -29,8 +29,6 @@ import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.PointerType -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass -import de.fraunhofer.aisec.cpg.passes.astParent import de.fraunhofer.aisec.cpg.test.analyze import de.fraunhofer.aisec.cpg.test.analyzeAndGetFirstTU import de.fraunhofer.aisec.cpg.test.assertFullName @@ -97,7 +95,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(tu) @@ -162,7 +159,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(result) @@ -186,7 +182,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(tu) @@ -205,7 +200,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(tu) @@ -283,7 +277,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(tu) @@ -320,7 +313,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(tu) @@ -342,7 +334,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(tu) @@ -413,7 +404,6 @@ class JVMLanguageFrontendTest { topLevel, true ) { - it.registerPass() it.registerLanguage() } assertNotNull(tu) 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 1fa1ee5f9d..1c2687fa68 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 @@ -871,31 +871,31 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : val ptrDerefExch = newUnaryOperator("*", postfix = false, prefix = true, rawNode = instr) ptrDerefExch.input = frontend.getOperandValueAtIndex(instr, 0) - exchOp.lhs = listOf(ptrDerefExch) + exchOp.lhs = mutableListOf(ptrDerefExch) when (operation) { LLVMAtomicRMWBinOpXchg -> { - exchOp.rhs = listOf(value) + exchOp.rhs = mutableListOf(value) } LLVMAtomicRMWBinOpFAdd, LLVMAtomicRMWBinOpAdd -> { val binaryOperator = newBinaryOperator("+", rawNode = instr) binaryOperator.lhs = ptrDeref binaryOperator.rhs = value - exchOp.rhs = listOf(binaryOperator) + exchOp.rhs = mutableListOf(binaryOperator) } LLVMAtomicRMWBinOpFSub, LLVMAtomicRMWBinOpSub -> { val binaryOperator = newBinaryOperator("-", rawNode = instr) binaryOperator.lhs = ptrDeref binaryOperator.rhs = value - exchOp.rhs = listOf(binaryOperator) + exchOp.rhs = mutableListOf(binaryOperator) } LLVMAtomicRMWBinOpAnd -> { val binaryOperator = newBinaryOperator("&", rawNode = instr) binaryOperator.lhs = ptrDeref binaryOperator.rhs = value - exchOp.rhs = listOf(binaryOperator) + exchOp.rhs = mutableListOf(binaryOperator) } LLVMAtomicRMWBinOpNand -> { val binaryOperator = newBinaryOperator("|", rawNode = instr) @@ -903,19 +903,19 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : binaryOperator.rhs = value val unaryOperator = newUnaryOperator("~", false, true, rawNode = instr) unaryOperator.input = binaryOperator - exchOp.rhs = listOf(unaryOperator) + exchOp.rhs = mutableListOf(unaryOperator) } LLVMAtomicRMWBinOpOr -> { val binaryOperator = newBinaryOperator("|", rawNode = instr) binaryOperator.lhs = ptrDeref binaryOperator.rhs = value - exchOp.rhs = listOf(binaryOperator) + exchOp.rhs = mutableListOf(binaryOperator) } LLVMAtomicRMWBinOpXor -> { val binaryOperator = newBinaryOperator("^", rawNode = instr) binaryOperator.lhs = ptrDeref binaryOperator.rhs = value - exchOp.rhs = listOf(binaryOperator) + exchOp.rhs = mutableListOf(binaryOperator) } LLVMAtomicRMWBinOpMax, LLVMAtomicRMWBinOpMin -> { @@ -938,7 +938,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : value, ty, ) - exchOp.rhs = listOf(conditional) + exchOp.rhs = mutableListOf(conditional) } LLVMAtomicRMWBinOpUMax, LLVMAtomicRMWBinOpUMin -> { @@ -968,7 +968,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : value, ty, ) - exchOp.rhs = listOf(conditional) + exchOp.rhs = mutableListOf(conditional) } else -> { throw TranslationException("LLVMAtomicRMWBinOp $operation not supported") 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 464f66452a..082de02842 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 @@ -227,7 +227,7 @@ class CompressLLVMPass(ctx: TranslationContext) : ComponentPass(ctx) { alreadyChecked.add(currentNode) // We exclude sub-try statements as they would mess up with the results val toAdd = - SubgraphWalker.getAstChildren(currentNode).filter { n -> + currentNode.astChildren.filter { n -> n !is TryStatement && !alreadyChecked.contains(n) && !worklist.contains(n) } worklist.addAll(toAdd) 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 a72c8f3cc6..86f8ea2b7a 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 @@ -230,7 +230,8 @@ class ExpressionHandler(frontend: PythonLanguageFrontend) : // Here we can not use node as raw node as it spans all keys and values lst += newKeyValueExpression( - key = node.keys[i]?.let { handle(it) }, + key = + node.keys[i]?.let { handle(it) } ?: newProblemExpression("missing key"), value = handle(node.values[i]), ) .codeAndLocationFromChildren(node) @@ -370,7 +371,7 @@ class ExpressionHandler(frontend: PythonLanguageFrontend) : } for (keyword in node.keywords) { - ret.addArgument(handle(keyword.value), keyword.arg) + ret.argumentEdges.add(handle(keyword.value)) { name = keyword.arg } } return ret diff --git a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/ExpressionHandler.kt b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/ExpressionHandler.kt index 7c85bbc6d5..c9089dd6ee 100644 --- a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/ExpressionHandler.kt +++ b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/ExpressionHandler.kt @@ -94,8 +94,8 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : val assign = newAssignExpression("=") // we need to build a reference out of the assignment node itself for our LHS - assign.lhs = listOf(handleINameNode(node)) - assign.rhs = listOf(this.handle(node.valueNode)) + assign.lhs = mutableListOf(handleINameNode(node)) + assign.rhs = mutableListOf(this.handle(node.valueNode)) return assign } @@ -104,8 +104,8 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : val assign = newAssignExpression("=") // we need to build a reference out of the assignment node itself for our LHS - assign.lhs = listOf(handleINameNode(node)) - assign.rhs = listOf(this.handle(node.valueNode)) + assign.lhs = mutableListOf(handleINameNode(node)) + assign.rhs = mutableListOf(this.handle(node.valueNode)) return assign } diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt index 818d6ca88b..635bc1e739 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt @@ -62,8 +62,10 @@ class ExpressionHandler(lang: TypeScriptLanguageFrontend) : } private fun handleJsxAttribute(node: TypeScriptNode): KeyValueExpression { - val key = node.children?.first()?.let { this.handle(it) } - val value = node.children?.last()?.let { this.handle(it) } + val key = + node.children?.first()?.let { this.handle(it) } ?: newProblemExpression("missing key") + val value = + node.children?.last()?.let { this.handle(it) } ?: newProblemExpression("missing value") return newKeyValueExpression(key, value, rawNode = node) } @@ -140,8 +142,10 @@ class ExpressionHandler(lang: TypeScriptLanguageFrontend) : } private fun handlePropertyAssignment(node: TypeScriptNode): KeyValueExpression { - val key = node.children?.first()?.let { this.handle(it) } - val value = node.children?.last()?.let { this.handle(it) } + val key = + node.children?.first()?.let { this.handle(it) } ?: newProblemExpression("missing key") + val value = + node.children?.last()?.let { this.handle(it) } ?: newProblemExpression("missing value") return newKeyValueExpression(key, value, rawNode = node) }