|
26 | 26 | package de.fraunhofer.aisec.cpg.passes.concepts
|
27 | 27 |
|
28 | 28 | import de.fraunhofer.aisec.cpg.TranslationContext
|
| 29 | +import de.fraunhofer.aisec.cpg.graph.Component |
29 | 30 | import de.fraunhofer.aisec.cpg.graph.Node
|
30 | 31 | import de.fraunhofer.aisec.cpg.graph.allEOGStarters
|
31 |
| -import de.fraunhofer.aisec.cpg.graph.component |
32 | 32 | import de.fraunhofer.aisec.cpg.graph.conceptNodes
|
33 | 33 | import de.fraunhofer.aisec.cpg.graph.concepts.Concept
|
34 | 34 | import de.fraunhofer.aisec.cpg.graph.concepts.Operation
|
35 | 35 | import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
|
36 | 36 | import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
|
37 |
| -import de.fraunhofer.aisec.cpg.passes.TranslationUnitPass |
| 37 | +import de.fraunhofer.aisec.cpg.passes.ComponentPass |
| 38 | +import de.fraunhofer.aisec.cpg.passes.ControlFlowSensitiveDFGPass |
| 39 | +import de.fraunhofer.aisec.cpg.passes.DynamicInvokeResolver |
| 40 | +import de.fraunhofer.aisec.cpg.passes.SymbolResolver |
| 41 | +import de.fraunhofer.aisec.cpg.passes.Task |
| 42 | +import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn |
| 43 | +import de.fraunhofer.aisec.cpg.passes.configuration.ExecuteBefore |
38 | 44 | import de.fraunhofer.aisec.cpg.processing.strategy.Strategy
|
39 | 45 |
|
| 46 | +typealias ConceptTask = Task<Component, ConceptPass> |
| 47 | + |
40 | 48 | /**
|
41 |
| - * An abstract pass that is used to identify and create [Concept] and [Operation] nodes in the |
42 |
| - * graph. |
| 49 | + * A pass that is used to identify and create [Concept] and [Operation] nodes in the graph. The idea |
| 50 | + * is that developers that add concepts do not need to implement a full pass, but can just implement |
| 51 | + * a small [Task] that handles the nodes relevant for the concept. |
43 | 52 | */
|
44 |
| -abstract class ConceptPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { |
| 53 | +@DependsOn(SymbolResolver::class) |
| 54 | +@DependsOn(ControlFlowSensitiveDFGPass::class) |
| 55 | +@ExecuteBefore(DynamicInvokeResolver::class) |
| 56 | +class ConceptPass(ctx: TranslationContext) : ComponentPass(ctx) { |
45 | 57 |
|
46 | 58 | lateinit var walker: SubgraphWalker.ScopedWalker
|
47 | 59 |
|
48 |
| - override fun accept(tu: TranslationUnitDeclaration) { |
49 |
| - ctx.currentComponent = tu.component |
| 60 | + override fun accept(c: Component) { |
| 61 | + ctx.currentComponent = c |
50 | 62 | walker = SubgraphWalker.ScopedWalker(ctx.scopeManager)
|
51 | 63 | walker.strategy = Strategy::EOG_FORWARD
|
52 |
| - walker.registerHandler { node -> handleNode(node, tu) } |
| 64 | + walker.registerHandler { node -> handleNode(node) } |
| 65 | + |
| 66 | + // Process nodes in our translation units in the order depending on their import |
| 67 | + // dependencies |
| 68 | + for (tu in (Strategy::TRANSLATION_UNITS_LEAST_IMPORTS)(c)) { |
| 69 | + log.debug("Processing concepts of translation unit {}", tu.name) |
53 | 70 |
|
54 |
| - // Gather all resolution EOG starters; and make sure they really do not have a |
55 |
| - // predecessor, otherwise we might analyze a node multiple times |
56 |
| - val nodes = tu.allEOGStarters.filter { it.prevEOGEdges.isEmpty() } |
| 71 | + // Gather all resolution EOG starters; and make sure they really do not have a |
| 72 | + // predecessor, otherwise we might analyze a node multiple times |
| 73 | + val nodes = tu.allEOGStarters.filter { it.prevEOGEdges.isEmpty() } |
57 | 74 |
|
58 |
| - for (node in nodes) { |
59 |
| - walker.iterate(node) |
| 75 | + for (node in nodes) { |
| 76 | + walker.iterate(node) |
| 77 | + } |
60 | 78 | }
|
61 | 79 | }
|
62 | 80 |
|
63 | 81 | /**
|
64 |
| - * This function is called for each node in the graph. It needs to be overridden by subclasses |
65 |
| - * to handle the specific node. |
66 |
| - */ |
67 |
| - abstract fun handleNode(node: Node, tu: TranslationUnitDeclaration) |
68 |
| - |
69 |
| - /** |
70 |
| - * Gets concept of type [T] for this [TranslationUnitDeclaration] or creates a new one if it |
71 |
| - * does not exist. |
| 82 | + * This function is called for each node in the graph. It will invoke all plugins that are |
| 83 | + * registered in the pass configuration. |
72 | 84 | */
|
73 |
| - internal inline fun <reified T : Concept> TranslationUnitDeclaration.getConceptOrCreate( |
74 |
| - noinline init: ((T) -> Unit)? = null |
75 |
| - ): T { |
76 |
| - var concept = this.conceptNodes.filterIsInstance<T>().singleOrNull() |
77 |
| - if (concept == null) { |
78 |
| - concept = T::class.constructors.first().call(this) |
79 |
| - init?.invoke(concept) |
| 85 | + fun handleNode(node: Node) { |
| 86 | + // Execute tasks |
| 87 | + for (task in tasks) { |
| 88 | + task.handleNode(node) |
80 | 89 | }
|
81 |
| - |
82 |
| - return concept |
83 | 90 | }
|
84 | 91 |
|
85 | 92 | override fun cleanup() {
|
86 | 93 | // Nothing to do
|
87 | 94 | }
|
88 | 95 | }
|
| 96 | + |
| 97 | +/** |
| 98 | + * Gets concept of type [T] for this [TranslationUnitDeclaration] or creates a new one if it does |
| 99 | + * not exist. |
| 100 | + */ |
| 101 | +inline fun <reified T : Concept> TranslationUnitDeclaration.getConceptOrCreate( |
| 102 | + noinline init: ((T) -> Unit)? = null |
| 103 | +): T { |
| 104 | + var concept = this.conceptNodes.filterIsInstance<T>().singleOrNull() |
| 105 | + if (concept == null) { |
| 106 | + concept = T::class.constructors.first().call(this) |
| 107 | + init?.invoke(concept) |
| 108 | + } |
| 109 | + |
| 110 | + return concept |
| 111 | +} |
0 commit comments