From 5c7b07d3de6ba32608e14a5a6f9583598a2b7d6a Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Fri, 8 Sep 2023 18:18:51 +0200 Subject: [PATCH] Less test errors --- .../aisec/cpg/TranslationConfiguration.kt | 1 + .../aisec/cpg/helpers/SubgraphWalker.kt | 17 ++-- .../cpg/passes/EvaluationOrderGraphPass.kt | 22 ----- .../aisec/cpg/passes/MagicFixItPass.kt | 93 +++++++++++++++++++ .../aisec/cpg/passes/SymbolResolver.kt | 60 +----------- .../frontends/cxx/CXXLanguageFrontendTest.kt | 2 + .../cpp/scope_variables.cpp | 6 +- 7 files changed, 114 insertions(+), 87 deletions(-) create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/MagicFixItPass.kt diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt index e2436ea703..f5749c85de 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt @@ -457,6 +457,7 @@ private constructor( fun defaultPasses(): Builder { registerPass() registerPass() + registerPass() registerPass() registerPass() registerPass() // creates EOG 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 49644cfa7d..afcaaf7ab6 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 @@ -39,8 +39,6 @@ import java.lang.reflect.Field import java.util.* import java.util.function.BiConsumer import java.util.function.Consumer -import java.util.function.Predicate -import java.util.stream.Collectors import org.neo4j.ogm.annotation.Relationship import org.slf4j.LoggerFactory @@ -258,6 +256,8 @@ object SubgraphWalker { var backlog: Deque? = null private set + var strategy: (Node) -> Iterator = Strategy::AST_FORWARD + /** * This callback is triggered whenever a new node is visited for the first time. This is the * place where usual graph manipulation will happen. The current node is the single argument @@ -316,10 +316,7 @@ object SubgraphWalker { Consumer { c: BiConsumer -> c.accept(current, parent) } ) val unseenChildren = - getAstChildren(current) - .stream() - .filter(Predicate.not { o: Node -> seen.contains(o) }) - .collect(Collectors.toList()) + strategy(current).asSequence().filter { it !in seen }.toList() seen.addAll(unseenChildren) unseenChildren.asReversed().forEach { child: Node -> (todo as ArrayDeque>).push(Pair(child, current)) @@ -359,6 +356,7 @@ object SubgraphWalker { * resolving declarations or other scope-related tasks. */ class ScopedWalker { + lateinit var strategy: (Node) -> Iterator private var walker: IterativeGraphWalker? = null private val scopeManager: ScopeManager @@ -366,8 +364,12 @@ object SubgraphWalker { scopeManager = lang.scopeManager } - constructor(scopeManager: ScopeManager) { + constructor( + scopeManager: ScopeManager, + strategy: (Node) -> Iterator = Strategy::AST_FORWARD + ) { this.scopeManager = scopeManager + this.strategy = strategy } /** @@ -399,6 +401,7 @@ object SubgraphWalker { */ fun iterate(root: Node) { walker = IterativeGraphWalker() + walker!!.strategy = this.strategy handlers.forEach { h -> walker?.registerOnNodeVisit { n -> handleNode(n, h) } } walker?.iterate(root) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt index 0a8a41a8e5..9d85400f09 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt @@ -1043,25 +1043,3 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa } } } - -/** - * This function injects the [next] node in-between the current [Node] and its [Node.nextEOG] nodes. - * This is needed if implicit nodes are created after the original EOG pass. - */ -fun Node.injectNextEOG(next: Node) { - // Store the references to the old "nextEOG" - val oldNext = this.nextEOG - - // Clear next EOG and set it to our new next - this.nextEOG = listOf(next) - // Add us to the prevEOG of next - next.prevEOG += this - // Add the old nextEOGs to the nextEOG of new next - next.nextEOG += oldNext - - // For each oldNext, replace "us" with the new next - oldNext.forEach { - it.prevEOGEdges.removeAll { edge -> edge.start == this } - it.prevEOG += next - } -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/MagicFixItPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/MagicFixItPass.kt new file mode 100644 index 0000000000..f33a0c6791 --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/MagicFixItPass.kt @@ -0,0 +1,93 @@ +/* + * 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.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.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.newConstructExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression +import de.fraunhofer.aisec.cpg.graph.types.recordDeclaration +import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker +import de.fraunhofer.aisec.cpg.passes.order.ExecuteBefore + +/** + * This [Pass] is magic. I think it's mostly used by C++, so we could make it specific. However, it + * also does certain things that the Go Extra Pass also does, so we could merge some aspects of it. + */ +@ExecuteBefore(EvaluationOrderGraphPass::class) +class MagicFixItPass(ctx: TranslationContext) : ComponentPass(ctx) { + override fun accept(component: Component) { + val walker = SubgraphWalker.ScopedWalker(ctx.scopeManager) + + walker.registerHandler(::fixInitializers) + for (tu in component.translationUnits) { + walker.iterate(tu) + } + } + + protected fun fixInitializers(node: Node?, currClass: RecordDeclaration?) { + if (node is VariableDeclaration) { + // check if we have the corresponding class for this type + val record = node.type.root.recordDeclaration + val typeString = node.type.root.name + if (record != null) { + val currInitializer = node.initializer + if (currInitializer == null && node.isImplicitInitializerAllowed) { + val initializer = node.newConstructExpression(typeString, "$typeString()") + initializer.type = node.type + initializer.isImplicit = true + node.initializer = initializer + node.templateParameters?.let { + SymbolResolver.addImplicitTemplateParametersToCall(it, initializer) + } + } else if ( + currInitializer !is ConstructExpression && + currInitializer is CallExpression && + currInitializer.name.localName == node.type.root.name.localName + ) { + // This should actually be a construct expression, not a call! + val arguments = currInitializer.arguments + val signature = arguments.map(Node::code).joinToString(", ") + val initializer = + node.newConstructExpression(typeString, "$typeString($signature)") + initializer.type = node.type + initializer.arguments = mutableListOf(*arguments.toTypedArray()) + initializer.isImplicit = true + node.initializer = initializer + currInitializer.disconnectFromGraph() + } + } + } + } + + override fun cleanup() { + // Nothing to do + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt index 652b857d0b..380844ec5a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt @@ -38,7 +38,6 @@ import de.fraunhofer.aisec.cpg.passes.inference.inferFunction import de.fraunhofer.aisec.cpg.passes.inference.inferMethod import de.fraunhofer.aisec.cpg.passes.inference.startInference import de.fraunhofer.aisec.cpg.passes.order.DependsOn -import de.fraunhofer.aisec.cpg.processing.IVisitor import de.fraunhofer.aisec.cpg.processing.strategy.Strategy import java.util.* import java.util.ArrayDeque @@ -94,12 +93,6 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { walker.iterate(tu) } - walker.clearCallbacks() - walker.registerHandler(::fixInitializers) - for (tu in component.translationUnits) { - walker.iterate(tu) - } - /*walker.registerHandler(::resolveFieldUsages) for (tu in component.translationUnits) { currentTU = tu @@ -119,24 +112,17 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { walker.iterate(tu) }*/ + walker.strategy = Strategy::EOG_FORWARD + walker.clearCallbacks() + walker.registerHandler(::resolveCalls) for (tu in component.translationUnits) { currentTU = tu - // gather all resolution start holders and their start nodes val nodes = tu.allChildren().flatMap { it.resolutionStartNodes }.toSet() for (node in nodes) { - // Traverse the EOG within the node - node.accept( - Strategy::EOG_FORWARD, - object : IVisitor() { - override fun visit(t: Node) { - scopeManager.jumpTo(t.scope) - resolveCalls(scopeManager.currentRecord, null, t) - } - } - ) + walker.iterate(node) } } } @@ -513,44 +499,6 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { ) } - protected fun fixInitializers(node: Node?, currClass: RecordDeclaration?) { - if (node is VariableDeclaration) { - // check if we have the corresponding class for this type - val record = node.type.root.recordDeclaration - val typeString = node.type.root.name - if (record != null) { - val currInitializer = node.initializer - if (currInitializer == null && node.isImplicitInitializerAllowed) { - val initializer = node.newConstructExpression(typeString, "$typeString()") - initializer.type = node.type - initializer.isImplicit = true - node.initializer = initializer - // We need to make sure that the initializer is EOG-connected - node.injectNextEOG(initializer) - node.templateParameters?.let { - addImplicitTemplateParametersToCall(it, initializer) - } - } else if ( - currInitializer !is ConstructExpression && - currInitializer is CallExpression && - currInitializer.name.localName == node.type.root.name.localName - ) { - // This should actually be a construct expression, not a call! - val arguments = currInitializer.arguments - val signature = arguments.map(Node::code).joinToString(", ") - val initializer = - node.newConstructExpression(typeString, "$typeString($signature)") - initializer.type = node.type - initializer.arguments = mutableListOf(*arguments.toTypedArray()) - initializer.isImplicit = true - node.initializer = initializer - node.injectNextEOG(initializer) - currInitializer.disconnectFromGraph() - } - } - } - } - protected fun resolveCalls(currClass: RecordDeclaration?, parent: Node?, node: Node?) { when (node) { is TranslationUnitDeclaration -> { diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt index 8d0a085abb..0e5ba2503c 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt @@ -1444,6 +1444,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), false) { it.registerPass() it.registerPass() + it.registerPass() it.registerPass() it.registerPass() it.registerPass() // creates EOG @@ -1491,6 +1492,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), false) { it.registerPass() it.registerPass() + it.registerPass() it.registerPass() it.registerPass() it.registerPass() // creates EOG diff --git a/cpg-language-cxx/src/test/resources/variables_extended/cpp/scope_variables.cpp b/cpg-language-cxx/src/test/resources/variables_extended/cpp/scope_variables.cpp index 0d5e395b66..2546c166a4 100644 --- a/cpg-language-cxx/src/test/resources/variables_extended/cpp/scope_variables.cpp +++ b/cpg-language-cxx/src/test/resources/variables_extended/cpp/scope_variables.cpp @@ -12,6 +12,8 @@ void printLog(string logId, string message){ cout << logId << ": " << message << endl; } +class error; + class ScopeVariables{ public: string varName = "instance_field"; @@ -44,8 +46,8 @@ class ScopeVariables{ } try { - throw string("exception_string"); - } catch (const string& varName) { + throw new error(); + } catch (const error& varName) { printLog("func2_catch_varName", varName); }; ScopeVariables scopeVariables;