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

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

/*
 * 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: 23 juin 2010
 * Time: 16:47:11
 */
package apparat.graph

import collection.mutable.ListBuffer
import apparat.bytecode.operations._
import apparat.bytecode.{BytecodeExceptionHandler, Marker, Bytecode, MarkerManager}
import mutable.{MutableAbstractOpBlockVertex, MutableBytecodeControlFlowGraph}

object ControlFlowGraphBytecodeBuilder {
	private def toBytecode[V <: BlockVertex[AbstractOp], G <: ControlFlowGraphLike[V]](cfg: G) = {
		val newGraph = cfg.optimized

		val vertices = GraphVerticesSorter(newGraph).vertices
		val verticesWithIndex = vertices.zipWithIndex

		val verticesIndexMap = {
			var map = Map.empty[V, Int]
			verticesWithIndex.foreach {p => map = map updated (p._1, p._2)}
			map
		}

		var elms = List.empty[ListBuffer[AbstractOp]]

		var vertexBlockMap = Map.empty[V, ListBuffer[AbstractOp]]

		val markers: MarkerManager = new MarkerManager()

		def addBlockFor(vertex: V) = {
			val lb = ListBuffer.empty[AbstractOp]
			lb ++= vertex.block
			elms = lb :: elms
			vertexBlockMap = vertexBlockMap updated (vertex, lb)

			val index = verticesIndexMap(vertex)
			val backEdge = newGraph.incomingOf(vertex).exists(e => {
				verticesIndexMap.getOrElse(e.startVertex, -1) >= index
			})
			if (backEdge) {
				// this block have at least a back edge
				// so add a Label as a first op if none exists
				lb.headOption match {
					case Some(op) => if (!op.isInstanceOf[Label]) {
						val label = Label()
						lb.insert(0, label)
						markers.forwardMarker(op, label)
					}
					case _ => lb.insert(0, Label())
				}
			}
			lb
		}
		vertices map addBlockFor _

		sealed class TryBlock(val startIdx: Int, val catchIdx: Int, val endIdx: Int) {
			def needMarker(blockIdx: Int) = blockIdx == startIdx || blockIdx == endIdx || blockIdx == catchIdx

			override def toString() = "TryBlock(" + startIdx + "," + endIdx + "," + catchIdx + ")"
		}

		// get all catch and also finally blocks
		var catchBlocksWithIndex = {
			@inline def f(l: Iterable[Edge[V]]) = {
				l.filter(_.kind == EdgeKind.Throw).size == l.size
			}
			@inline def sortCatchBlock(x: (V, Int), y: (V, Int)) = x._2 > y._2

			// all catch block have only incoming throw edge
			// n.b. a finally is also a catch block
			verticesWithIndex.filter(vi => f(newGraph.incomingOf(vi._1))).sortWith(sortCatchBlock)
		}

		var tryCatchList = List.empty[TryBlock]

		for (catchBlock <- catchBlocksWithIndex) {
			val throws = newGraph.incomingOf(catchBlock._1).map(e => verticesIndexMap(e.startVertex)).toList.sortWith((i1: Int, i2: Int) => i1 < i2)
			tryCatchList = new TryBlock(throws.head, catchBlock._2, throws.last) :: tryCatchList
		}

		// check if a block immediately follow the other one
		@inline def inSequence(v1: V, v2: V) = {
			verticesIndexMap.get(v2) match {
				case Some(i) => (i - verticesIndexMap(v1)) == 1
				case _ => false
			}
		}

		def getMarkerFor(vertex: V) = {
			var lst = vertexBlockMap(vertex)
			if (lst.isEmpty) lst += Nop()
			markers.mark(lst.head)
		}

		def addLabelIfNeeded(from: V, to: V) {
			if (verticesIndexMap(from) > verticesIndexMap(to)) {
				val toBlock = vertexBlockMap(to)
				toBlock.headOption match {
					case Some(op) => if (!op.isInstanceOf[Label]) {
						val label = Label()
						toBlock.insert(0, label)
						markers.forwardMarker(op, label)
					}
					case _ => toBlock.insert(0, Label())
				}
			}
		}

		def patchJump(from: V, to: V, invertCondition: Boolean = false) = {
			addLabelIfNeeded(from, to)

			val marker = getMarkerFor(to)

			val target = vertexBlockMap(from)
			if (target.isEmpty)
				target += Nop()

			val oldOp = target.lastOption
			target.last match {
				case op: AbstractConditionalOp => {
					target(target.length - 1) = if (invertCondition)
						Op.invertCopyConditionalOp(op, marker)
					else
						Op.copyConditionalOp(op, marker)
				}
				case op: Jump => target(target.length - 1) = Jump(marker)
				case _ => target += Jump(marker)
			}
			oldOp.map(markers.forwardMarker(_, target.last))
		}

		def appendJump(from: V, to: V) = {
			addLabelIfNeeded(from, to)

			val marker = getMarkerFor(to)

			val target = vertexBlockMap(from)

			target += Jump(marker)
		}


		sealed class LookupSwitchContainer(val size: Int) {
			private val cases: Array[Marker] = new Array(size)
			private var default: Option[Marker] = None
			private var caseLeft = size + 1

			def addCase(startVertex: V, endVertex: V, isDefault: Boolean = false, index: Int = -1): Boolean = {
				if (isDefault) {
					if (default == None) {
						caseLeft -= 1
						default = Some(getMarkerFor(endVertex))
					} else
						error("A LookupSwitch can't have multiple default branch")
				} else {
					caseLeft -= 1
					if (caseLeft < 0)
						error("Too many case into LookupSwitch for " + startVertex)
					else {
						if (index < 0)
							error("Case index can't be < 0")
						else if (index >= cases.length) {
							error("Case index out of bounds : " + index)
						} else
							cases(index) = getMarkerFor(endVertex)
					}
				}
				isDone
			}

			def isDone = caseLeft == 0

			def apply(): LookupSwitch = if (!isDone) error("Incomplete LookupSwitch") else LookupSwitch(default.get, cases)
		}

		// hold a LookupSwitchContainer while building it
		var lookupSwitchBuildMap: Map[V, LookupSwitchContainer] = Map.empty

		def patchLookupSwitch(startVertex: V, endVertex: V, isDefault: Boolean = false, index: Int = -1) = {
			if (!lookupSwitchBuildMap.contains(startVertex))
				lookupSwitchBuildMap = lookupSwitchBuildMap updated (startVertex, new LookupSwitchContainer(newGraph.outdegreeOf(startVertex) - 1))

			val lsContainer = lookupSwitchBuildMap(startVertex)

			if (lsContainer.addCase(startVertex, endVertex, isDefault, index)) {
				val target = vertexBlockMap(startVertex)
				val lastOp = target.lastOption
				lastOp match {
					case Some(op) if (op.opCode == Op.lookupswitch) => target(target.length - 1) = lsContainer()
					case _ => target += lsContainer()
				}
				lookupSwitchBuildMap = lookupSwitchBuildMap - startVertex
				lastOp.map(markers.forwardMarker(_, target.last))
			}
		}

		var edgesVisited = Set.empty[Edge[V]]

		vertices.foreach(
			vertex => {
				for (edge <- newGraph.outgoingOf(vertex) if (!edgesVisited.contains(edge))) {
					edgesVisited += edge
					edge match {
						case TrueEdge(start, end) => {
							if (inSequence(start, end)) {
								// need to invert the condition
								// so we can avoid a jump
								// have to found the false edge
								// and mark the target op
								newGraph.outgoingOf(start).find(_.kind == EdgeKind.False) match {
									case Some(falseEdge) => patchJump(start, falseEdge.endVertex, true)
									case _ => error("Missing false edge : " + edge)
								}
							} else {
								patchJump(start, end)
								// check if the false edge follow the block if not add a jump
								newGraph.outgoingOf(start).find(_.kind == EdgeKind.False) match {
									case Some(falseEdge) if (!inSequence(start, falseEdge.endVertex)) => {
										appendJump(start, falseEdge.endVertex)
									}
									case _ =>
								}
							}
						}
						case FalseEdge(start, end) =>
						case ThrowEdge(start, end) =>
						case DefaultCaseEdge(start, end) => patchLookupSwitch(start, end, true)
						case NumberedCaseEdge(start, end, n) => patchLookupSwitch(start, end, false, n)
						case ReturnEdge(start, end) =>
						case _ => if (!inSequence(edge.startVertex, edge.endVertex)) appendJump(edge.startVertex, edge.endVertex)
					}
				}
			})

		// mark and patch exception
		var exceptionHandlers = List.empty[BytecodeExceptionHandler]

		for (tryCatch <- tryCatchList) {
			val catchBlock = vertexBlockMap(vertices(tryCatch.catchIdx))
			val tryStartBlock = vertexBlockMap(vertices(tryCatch.startIdx))
			val tryEndBlock = vertexBlockMap(vertices(tryCatch.endIdx))
			val markerStart = markers.mark(tryStartBlock.head)

			val markerEnd = if (tryCatch.endIdx < (tryCatch.catchIdx - 1)) {
				markers.mark(tryEndBlock.last)
			} else {
				tryEndBlock.last match {
					case branch: OpWithMarker => {
						markers.mark(branch)
					}
					case _ => {
						var m = markers.mark(Label())
						val jmp = Jump(m)
						markers.forwardMarker(m.op.get, jmp)
						tryEndBlock += jmp
						markers.mark(jmp)
					}
				}
			}

			val markerCatch = markers.mark(catchBlock.head)

			//patch newCatch
			val opIndex = catchBlock.indexWhere(_.isInstanceOf[NewCatch])
			if (opIndex >= 0) {
				val op = catchBlock(opIndex)
				val exceptionHandler = op.asInstanceOf[NewCatch].exceptionHandler
				val newExceptionHandler = new BytecodeExceptionHandler(markerStart, markerEnd, markerCatch, exceptionHandler.typeName, exceptionHandler.varName)
				catchBlock(opIndex) = NewCatch(newExceptionHandler)
				exceptionHandlers = newExceptionHandler :: exceptionHandlers
			} else
				error("Internal error : Missing NewCatch operation")
		}

		// N.B beware of the order of the exceptionHandlers
		// most inner first
		new Bytecode(elms.reverse.flatMap(ops => ops), markers, exceptionHandlers.reverse.toArray, None)
	}

	def apply(cfg: MutableBytecodeControlFlowGraph) = {
		toBytecode[MutableAbstractOpBlockVertex, MutableBytecodeControlFlowGraph](cfg)
	}

	def apply[V <: BlockVertex[AbstractOp]](cfg: BytecodeControlFlowGraph[V]) = {
		toBytecode[V, BytecodeControlFlowGraph[V]](cfg)
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy