diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt index 2bb1726df8..cc0a5d2d3f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/EOGWorklist.kt @@ -154,6 +154,14 @@ class Worklist() { /** A list of all nodes which have already been visited. */ private val alreadySeen = IdentitySet() + enum class EvaluationState { + WIDENING, + NARROWING, + DONE + } + + val evaluationStateMap = IdentityHashMap() + constructor( globalState: IdentityHashMap> = IdentityHashMap>() ) : this() { @@ -210,7 +218,7 @@ class Worklist() { /** Removes a [Node] from the worklist and returns the [Node] together with its [State] */ fun pop(): Pair> { - val node = nodeOrder.removeFirst() + val node = nodeOrder.removeLast() alreadySeen.add(node.first) return node } @@ -218,6 +226,16 @@ class Worklist() { /** Checks if [currentNode] has already been visited before. */ fun hasAlreadySeen(currentNode: K) = currentNode in alreadySeen + /** Checks if [currentNode] needs to be widened. */ + fun needsWidening(currentNode: K) = evaluationStateMap[currentNode] == EvaluationState.WIDENING + + /** Checks if [currentNode] needs to be narrowed. */ + fun needsNarrowing(currentNode: K) = + evaluationStateMap[currentNode] == EvaluationState.NARROWING + + /** Checks if [currentNode] should not be changed anymore. */ + fun isDone(currentNode: K) = evaluationStateMap[currentNode] == EvaluationState.DONE + /** Computes the meet over paths for all the states in [globalState]. */ fun mop(): State? { val firstKey = globalState.keys.firstOrNull() @@ -247,47 +265,12 @@ inline fun iterateEOG( return iterateEOG(startNode, startState) { k, s, _ -> transformation(k, s) } } -/** - * Iterates through the worklist of the Evaluation Order Graph starting at [startNode] and with the - * [State] [startState]. For each node, the [transformation] is applied which should update the - * state. - * - * [transformation] receives the current [Node] popped from the worklist, the [State] at this node - * which is considered for this analysis and even the current [Worklist]. The worklist is given if - * we have to add more elements out-of-order e.g. because the EOG is traversed in an order which is - * not useful for this analysis. The [transformation] has to return the updated [State]. - */ inline fun iterateEOG( startNode: K, startState: State, transformation: (K, State, Worklist) -> State ): State? { - val initialState = IdentityHashMap>() - initialState[startNode] = startState - val worklist = Worklist(initialState) - worklist.push(startNode, startState) - - while (worklist.isNotEmpty()) { - val (nextNode, state) = worklist.pop() - - // This should check if we're not near the beginning/end of a basic block (i.e., there are - // no merge points or branches of the EOG nearby). If that's the case, we just parse the - // whole basic block and do not want to duplicate the state. Near the beginning/end, we do - // want to copy the state to avoid terminating the iteration too early by messing up with - // the state-changing checks. - val insideBB = - (nextNode.nextEOG.size == 1 && nextNode.prevEOG.singleOrNull()?.nextEOG?.size == 1) - val newState = - transformation(nextNode, if (insideBB) state else state.duplicate(), worklist) - if (worklist.update(nextNode, newState)) { - nextNode.nextEOG.forEach { - if (it is K) { - worklist.push(it, newState) - } - } - } - } - return worklist.mop() + return iterateEOG(startNode, startState, transformation, null) } inline fun , N : Any, V> iterateEOG( @@ -334,3 +317,48 @@ inline fun , N : Any, V> iterateEOG( } return worklist.mop() } + +/** + * Iterates through the worklist of the Evaluation Order Graph starting at [startNode] and with the + * [State] [startState]. For each node, the [transformation] is applied which should update the + * state. When the [until] node is reached, its successors are not added to the worklist. + * + * [transformation] receives the current [Node] popped from the worklist, the [State] at this node + * which is considered for this analysis and even the current [Worklist]. The worklist is given if + * we have to add more elements out-of-order e.g. because the EOG is traversed in an order which is + * not useful for this analysis. The [transformation] has to return the updated [State]. + */ +inline fun iterateEOG( + startNode: K, + startState: State, + transformation: (K, State, Worklist) -> State, + until: Node? +): State? { + val initialState = IdentityHashMap>() + initialState[startNode] = startState + val worklist = Worklist(initialState) + worklist.push(startNode, startState) + + while (worklist.isNotEmpty()) { + val (nextNode, state) = worklist.pop() + + // This should check if we're not near the beginning/end of a basic block (i.e., there are + // no merge points or branches of the EOG nearby). If that's the case, we just parse the + // whole basic block and do not want to duplicate the state. Near the beginning/end, we do + // want to copy the state to avoid terminating the iteration too early by messing up with + // the state-changing checks. + val insideBB = + (nextNode.nextEOG.size == 1 && nextNode.prevEOG.singleOrNull()?.nextEOG?.size == 1) + val newState = + transformation(nextNode, if (insideBB) state else state.duplicate(), worklist) + + if (worklist.update(nextNode, newState) && nextNode != until) { + nextNode.nextEOG.forEach { + if (it is K && !worklist.isDone(it)) { + worklist.push(it, newState) + } + } + } + } + return worklist.mop() +}