All Downloads are FREE. Search and download functionalities are using the official Maven repository.

apparat.graph.GraphVerticesSorter.scala Maven / Gradle / Ivy

package apparat.graph

import annotation.tailrec
import immutable.ImmutableAbstractOpBlockVertex
import mutable.MutableAbstractOpBlockVertex
import apparat.bytecode.operations.{NewCatch, Op}
import apparat.abc.{AbcNamespace, AbcQName}
/*
 * This file is part of Apparat.
 * 
 * Apparat is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Apparat is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Apparat. If not, see .
 * 
 * Copyright (C) 2009 Joa Ebert
 * http://www.joa-ebert.com/
 * 
 * User: Patrick Le Clec'h
 * Date: 5 mai 2010
 * Time: 16:32:32
 */


final class GraphVerticesSorter[V](graph: GraphLike[V]) {
	private lazy val entry: V = graph match {
		case controlFlow: ControlFlow[_] => controlFlow.entryVertex.asInstanceOf[V]
		case _ => graph.verticesIterator find (vertex => (graph indegreeOf vertex) == 0) match {
			case Some(vertex) => vertex
			case None => error("No vertex with indegree(v) == 0 found.")
		}
	}

	private lazy val exit: V = graph match {
		case controlFlow: ControlFlow[_] => controlFlow.exitVertex.asInstanceOf[V]
		case _ => graph.verticesIterator find (vertex => (graph outdegreeOf vertex) == 0) match {
			case Some(vertex) => vertex
			case None => error("No vertex with outdegree(v) == 0 found.")
		}
	}

	lazy val vertices = getVertices(entry, graph.verticesIterator.toList).filterNot(p => p == entry || p == exit)

	private lazy val globalCatchQName = AbcQName(Symbol(null), AbcNamespace(0, Symbol(null)))

	private def isGlobalCatch(v: V) = v match {
		case iaobv: ImmutableAbstractOpBlockVertex => {
			iaobv.block.find(op => op.opCode == Op.newcatch) match {
				case Some(op: NewCatch) if (op.exceptionHandler.typeName == globalCatchQName) => true
				case _ => false
			}
		}
		case maobv: MutableAbstractOpBlockVertex => {
			maobv.block.find(op => op.opCode == Op.newcatch) match {
				case Some(op: NewCatch) if (op.exceptionHandler.typeName == globalCatchQName) => true
				case _ => false
			}
		}
		case _ => false
	}

	private def getVertices(firstVertex: V, verticesToConsider: List[V]): List[V] = {
		// Sort the vertices using a topological sort and when finding a SCC reapply the algorithm on the SCC
		// greatly inspired from http://www.woodmann.com/forum/entry.php?95-Control-Flow-Deobfuscation-Part-3
		// the challenging part is to correctly ordered exception block

		var order = List.empty[V]
		var stack = List.empty[V]
		var currIndex = 0
		var indexMap = Map.empty[V, Int]
		var lowMap = Map.empty[V, Int]
		var visitedMap = Map.empty[V, Boolean]

		def sortIncomingVertices(l: List[V]) = {
			@inline def sort(v1: V, v2: V) = {
				if (graph.outgoingOf(v1).view.exists(_.kind == EdgeKind.Throw))
					true
				else if (graph.outgoingOf(v2).view.exists(_.kind == EdgeKind.Throw))
					false
				else if (graph.incomingOf(v1).view.exists(_.kind == EdgeKind.Throw))
					true
				else
					false
			}
			l.sortWith(sort)
		}

		def sortOutgoingVertices(e1: graph.E, e2: graph.E) = {
			if (e1.kind == EdgeKind.Throw) {
				if (e2.kind == EdgeKind.Throw) {
					isGlobalCatch(e1.endVertex)
				} else {
					!graph.successorsOf(e2.endVertex).view.exists(_ == e1.endVertex)
				}
			} else if (e2.kind != EdgeKind.Throw) {
				true
			} else {
				graph.successorsOf(e1.endVertex).view.exists(_ == e2.endVertex)
			}
		}

		def visit(vertex: V): Unit = {
			stack = vertex :: stack

			lowMap = lowMap updated (vertex, currIndex)
			indexMap = indexMap updated (vertex, currIndex)

			currIndex += 1

			for (child <- graph.outgoingOf(vertex).toList.sortWith(sortOutgoingVertices).map(e => e.endVertex) if (verticesToConsider.contains(child))) {
				if (visitedMap.contains(child)) {
					if (!visitedMap(child)) {
						visitedMap -= child
						visit(child)
						lowMap = lowMap updated (vertex, math.min(lowMap(vertex), lowMap(child)))
					}
				} else {
					lowMap = lowMap updated (vertex, math.min(lowMap(vertex), indexMap(child)))
				}
			}

			if (lowMap(vertex) == indexMap(vertex)) {
				/* we found an SCC */
				var scc = List.empty[V]
				@tailrec def sccLoop(): Unit = {
					val popped = stack.head
					stack = stack.tail
					scc = popped :: scc
					visitedMap = visitedMap updated (popped, true)
					if (popped != vertex)
						sccLoop()
				}
				sccLoop()

				if (scc.size == 1)
					order = vertex :: order
				else {
					val sccSorted = sortIncomingVertices(scc)
					order = getVertices(sccSorted.head, sccSorted) ::: order
				}
			}
		}

		for (v <- verticesToConsider) visitedMap = visitedMap updated (v, false)

		visitedMap = visitedMap updated (firstVertex, true)

		for (child <- graph.outgoingOf(firstVertex).toList.sortWith(sortOutgoingVertices).map(e => e.endVertex) if (verticesToConsider.contains(child))) {
			if (!visitedMap.getOrElse(child, true))
				visit(child)
		}
		firstVertex :: order
	}
}

object GraphVerticesSorter {
	def apply[V](graph: GraphLike[V]) = new GraphVerticesSorter(graph)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy