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 cf8976c1a2d..8545b8396c2 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 @@ -36,6 +36,7 @@ import de.fraunhofer.aisec.cpg.graph.types.FunctionType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.passes.executePass +import de.fraunhofer.aisec.cpg.passes.executePassesInParallel fun LanguageFrontend<*, *>.translationResult( init: TranslationResult.() -> Unit @@ -49,7 +50,13 @@ fun LanguageFrontend<*, *>.translationResult( node.addComponent(component) init(node) - ctx.config.registeredPasses.flatten().forEach { executePass(it, ctx, node, listOf()) } + if (ctx.config.useParallelPasses) { + for (list in ctx.config.registeredPasses) { + executePassesInParallel(list, ctx, node, listOf()) + } + } else { + ctx.config.registeredPasses.flatten().forEach { executePass(it, ctx, node, listOf()) } + } return node } 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 b27d2dd259d..c3c30a9dbcd 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 @@ -36,12 +36,14 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.passes.PassTarget import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship /** Represents the declaration or definition of a function. */ -open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, ResolutionStartHolder { +open class FunctionDeclaration : + ValueDeclaration(), DeclarationHolder, ResolutionStartHolder, PassTarget { /** The function body. Usually a [Block]. */ @AST var body: Statement? = null diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt index 3d316742f7f..0e54ebed5c1 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt @@ -45,12 +45,13 @@ import kotlin.contracts.contract @OptIn(ExperimentalContracts::class) @DependsOn(EvaluationOrderGraphPass::class) @DependsOn(DFGPass::class) -open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { +open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : FunctionPass(ctx) { class Configuration( /** - * This specifies the maximum complexity (as calculated per [Statement.cyclomaticComplexity] - * a [FunctionDeclaration] must have in order to be considered. + * This specifies the maximum complexity (as calculated per + * [Statement.cyclomaticComplexity]) a [FunctionDeclaration] must have in order to be + * considered. */ var maxComplexity: Int? = null ) : PassConfiguration() @@ -59,65 +60,55 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : TranslationUni // Nothing to do } - override fun accept(tu: TranslationUnitDeclaration) { - tu.functions.forEach(::handle) - } - - /** - * We perform the actions for each [FunctionDeclaration]. - * - * @param node every node in the TranslationResult - */ - protected fun handle(node: Node) { + /** We perform the actions for each [FunctionDeclaration]. */ + override fun accept(node: FunctionDeclaration) { val max = passConfig()?.maxComplexity - if (node is FunctionDeclaration) { - // Skip empty functions - if (node.body == null) { - return - } + // Skip empty functions + if (node.body == null) { + return + } - // Calculate the complexity of the function and see, if it exceeds our threshold - if (max != null) { - val c = node.body?.cyclomaticComplexity ?: 0 - if (c > max) { - log.info( - "Ignoring function ${node.name} because its complexity (${c}) is greater than the configured maximum (${max})" - ) - return - } + // Calculate the complexity of the function and see, if it exceeds our threshold + if (max != null) { + val c = node.body?.cyclomaticComplexity ?: 0 + if (c > max) { + log.info( + "Ignoring function ${node.name} because its complexity (${c}) is greater than the configured maximum (${max})" + ) + return } + } - clearFlowsOfVariableDeclarations(node) - val startState = DFGPassState>() + clearFlowsOfVariableDeclarations(node) + val startState = DFGPassState>() - startState.declarationsState.push(node, PowersetLattice(identitySetOf())) - val finalState = - iterateEOG(node.nextEOGEdges, startState, ::transfer) as? DFGPassState ?: return + startState.declarationsState.push(node, PowersetLattice(identitySetOf())) + val finalState = + iterateEOG(node.nextEOGEdges, startState, ::transfer) as? DFGPassState ?: return - removeUnreachableImplicitReturnStatement( - node, - finalState.returnStatements.values.flatMap { - it.elements.filterIsInstance() - } - ) + removeUnreachableImplicitReturnStatement( + node, + finalState.returnStatements.values.flatMap { + it.elements.filterIsInstance() + } + ) - for ((key, value) in finalState.generalState) { - if (key is TupleDeclaration) { - // We need a little hack for tuple statements to set the index. We have the - // outer part (i.e., the tuple) here, but we generate the DFG edges to the - // elements. We have the indices here, so it's amazing. - key.elements.forEachIndexed { i, element -> - element.addAllPrevDFG( - value.elements.filterNot { it is VariableDeclaration && key == it }, - mutableMapOf(Properties.INDEX to i) - ) - } - } else { - key.addAllPrevDFG( - value.elements.filterNot { it is VariableDeclaration && key == it } + for ((key, value) in finalState.generalState) { + if (key is TupleDeclaration) { + // We need a little hack for tuple statements to set the index. We have the + // outer part (i.e., the tuple) here, but we generate the DFG edges to the + // elements. We have the indices here, so it's amazing. + key.elements.forEachIndexed { i, element -> + element.addAllPrevDFG( + value.elements.filterNot { it is VariableDeclaration && key == it }, + mutableMapOf(Properties.INDEX to i) ) } + } else { + key.addAllPrevDFG( + value.elements.filterNot { it is VariableDeclaration && key == it } + ) } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt index d174b178b46..f1b0c6ca245 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.helpers.Benchmark import de.fraunhofer.aisec.cpg.passes.order.RequiredFrontend @@ -59,9 +60,17 @@ abstract class ComponentPass(ctx: TranslationContext) : Pass(ctx) */ abstract class TranslationUnitPass(ctx: TranslationContext) : Pass(ctx) +/** + * A [FunctionPass] is a pass that operates on a [FunctionDeclaration]. If used with [executePass], + * one [Pass] object is instantiated for each [FunctionDeclaration] in each + * [TranslationUnitDeclaration] in each [Component]. + */ +abstract class FunctionPass(ctx: TranslationContext) : Pass(ctx) + /** * A pass target is an interface for a [Node] on which a [Pass] can operate, it should only be - * implemented by [TranslationResult], [Component] and [TranslationUnitDeclaration]. + * implemented by [TranslationResult], [Component], [TranslationUnitDeclaration] and + * [FunctionDeclaration]. */ interface PassTarget @@ -197,6 +206,14 @@ fun executePass( result.components.flatMap { it.translationUnits }, executedFrontends ) + is FunctionPass -> { + consumeTargets( + (prototype as FunctionPass)::class, + ctx, + result.components.flatMap { it.translationUnits.flatMap { it.functions } }, + executedFrontends + ) + } } bench.stop() 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 7e250c1b647..ead2db2230e 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 @@ -823,6 +823,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() + .useParallelPasses(true) .registerLanguage(TestLanguage(".")) .build() ) =