summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@epfl.ch>2009-12-01 08:28:07 +0000
committerLukas Rytz <lukas.rytz@epfl.ch>2009-12-01 08:28:07 +0000
commit0f17201b1005b85a8bbfd6be668fd9ffcc782375 (patch)
treef887faae4290d56e1868f81885b2c239d1ffdd54 /src
parent43c13143331d00ea78369ce41caa29e46752a69d (diff)
downloadscala-0f17201b1005b85a8bbfd6be668fd9ffcc782375.tar.gz
scala-0f17201b1005b85a8bbfd6be668fd9ffcc782375.tar.bz2
scala-0f17201b1005b85a8bbfd6be668fd9ffcc782375.zip
fix msil code generation for exception handlers.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala17
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala98
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala142
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala929
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala4
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala8
6 files changed, 497 insertions, 701 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
index b708d4df80..30060cc9eb 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
@@ -460,6 +460,23 @@ trait BasicBlocks {
succs
}
+ def directSuccessors: List[BasicBlock] = {
+ if (isEmpty) Nil else lastInstruction match {
+ case JUMP(whereto) => List(whereto)
+ case CJUMP(success, failure, _, _) => failure :: success :: Nil
+ case CZJUMP(success, failure, _, _) => failure :: success :: Nil
+ case SWITCH(_, labels) => labels
+ case RETURN(_) => Nil
+ case THROW() => Nil
+ case _ =>
+ if (closed) {
+ dump
+ global.abort("The last instruction is not a control flow instruction: " + lastInstruction)
+ }
+ else Nil
+ }
+ }
+
/** Return a list of successors for 'b' that come from exception handlers
* covering b's (non-exceptional) successors. These exception handlers
* might not cover 'b' itself. This situation corresponds to an
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index d4b00d1638..29dd06759a 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -320,7 +320,7 @@ abstract class GenICode extends SubComponent {
ctx2
}, List(
// tree.tpe / fun.tpe is object, which is no longer true after this transformation
- (NoSymbol, expectedType, exhCtx => {
+ (ThrowableClass, expectedType, exhCtx => {
exhCtx.bb.emit(Seq(
LOAD_LOCAL(monitor),
MONITOR_EXIT() setPos tree.pos,
@@ -1469,11 +1469,11 @@ abstract class GenICode extends SubComponent {
val pred = block.predecessors;
log("Preds: " + pred + " of " + block + " (" + optCont + ")");
pred foreach { p =>
+ changed = true
p.lastInstruction match {
- case CJUMP(succ, fail, cond, kind) =>
+ case CJUMP(succ, fail, cond, kind) if (succ == block || fail == block) =>
if (settings.debug.value)
log("Pruning empty if branch.");
- changed = true
p.replaceInstruction(p.lastInstruction,
if (block == succ)
if (block == fail)
@@ -1485,10 +1485,9 @@ abstract class GenICode extends SubComponent {
else
abort("Could not find block in preds: " + method + " " + block + " " + pred + " " + p))
- case CZJUMP(succ, fail, cond, kind) =>
+ case CZJUMP(succ, fail, cond, kind) if (succ == block || fail == block) =>
if (settings.debug.value)
log("Pruning empty ifz branch.");
- changed = true
p.replaceInstruction(p.lastInstruction,
if (block == succ)
if (block == fail)
@@ -1500,20 +1499,22 @@ abstract class GenICode extends SubComponent {
else
abort("Could not find block in preds"))
- case JUMP(b) =>
+ case JUMP(b) if (b == block) =>
if (settings.debug.value)
log("Pruning empty JMP branch.");
- changed = true
val replaced = p.replaceInstruction(p.lastInstruction, JUMP(cont))
if (settings.debug.value)
assert(replaced, "Didn't find p.lastInstruction")
- case SWITCH(tags, labels) =>
+ case SWITCH(tags, labels) if (labels contains block) =>
if (settings.debug.value)
log("Pruning empty SWITCH branch.");
- changed = true
p.replaceInstruction(p.lastInstruction,
SWITCH(tags, labels map (l => if (l == block) cont else l)))
+
+ // the last instr of the predecessor `p` is not a jump to the block `block`.
+ // this happens when `block` is part of an exception handler covering `b`.
+ case _ => ()
}
}
if (changed) {
@@ -1653,8 +1654,8 @@ abstract class GenICode extends SubComponent {
/** The current monitors or finalizers, to be cleaned up upon `return'. */
var cleanups: List[Cleanup] = Nil
- /** The current exception handler, when we generate code for one. */
- var currentExceptionHandler: Option[ExceptionHandler] = None
+ /** The exception handlers we are currently generating code for */
+ var currentExceptionHandlers: List[ExceptionHandler] = Nil
/** The current local variable scope. */
var scope: Scope = EmptyScope
@@ -1685,7 +1686,7 @@ abstract class GenICode extends SubComponent {
this.handlers = other.handlers
this.handlerCount = other.handlerCount
this.cleanups = other.cleanups
- this.currentExceptionHandler = other.currentExceptionHandler
+ this.currentExceptionHandlers = other.currentExceptionHandlers
this.scope = other.scope
}
@@ -1754,10 +1755,7 @@ abstract class GenICode extends SubComponent {
def newBlock: Context = {
val block = method.code.newBlock
handlers foreach (h => h addCoveredBlock block)
- currentExceptionHandler match {
- case Some(e) => e.addBlock(block)
- case None => ()
- }
+ currentExceptionHandlers foreach (h => h.addBlock(block))
block.varsInScope = new HashSet()
block.varsInScope ++= scope.varsInScope
new Context(this) setBasicBlock block
@@ -1804,12 +1802,16 @@ abstract class GenICode extends SubComponent {
* exception handler.
*/
def enterHandler(exh: ExceptionHandler): Context = {
- currentExceptionHandler = Some(exh)
+ currentExceptionHandlers = exh :: currentExceptionHandlers
val ctx = newBlock
exh.setStartBlock(ctx.bb)
ctx
}
+ def endHandler() {
+ currentExceptionHandlers = currentExceptionHandlers.tail
+ }
+
/** Remove the given handler from the list of active exception handlers. */
def removeHandler(exh: ExceptionHandler): Unit = {
assert(handlerCount > 0 && handlers.head == exh,
@@ -1853,7 +1855,7 @@ abstract class GenICode extends SubComponent {
def Try(body: Context => Context,
handlers: List[(Symbol, TypeKind, (Context => Context))],
finalizer: Tree,
- tree: Tree) = {
+ tree: Tree) = if (forMSIL) TryMsil(body, handlers, finalizer, tree) else {
val outerCtx = this.dup // context for generating exception handlers, covered by finalizer
val finalizerCtx = this.dup // context for generating finalizer handler
@@ -1893,6 +1895,7 @@ abstract class GenICode extends SubComponent {
ctx1.bb.emit(THROW());
ctx1.bb.enterIgnoreMode;
ctx1.bb.close
+ finalizerCtx.endHandler()
exh
}) else None
@@ -1905,6 +1908,7 @@ abstract class GenICode extends SubComponent {
val ctx2 = emitFinalizer(ctx1)
ctx2.bb.emit(JUMP(afterCtx.bb))
ctx2.bb.close
+ outerCtx.endHandler()
exh
}
val bodyCtx = this.newBlock
@@ -1922,6 +1926,64 @@ abstract class GenICode extends SubComponent {
afterCtx
}
+
+
+ /** try-catch-finally blocks are actually simpler to emit in MSIL, because there
+ * is support for `finally` in bytecode.
+ *
+ * A
+ * try { .. } catch { .. } finally { .. }
+ * blocks is de-sugared into
+ * try { try { ..} catch { .. } } finally { .. }
+ *
+ * A `finally` block is represented exactly the same as an exception handler, but
+ * with `NoSymbol` as the exception class. The covered blocks are all blocks of
+ * the `try { .. } catch { .. }`.
+ */
+ def TryMsil(body: Context => Context,
+ handlers: List[(Symbol, TypeKind, (Context => Context))],
+ finalizer: Tree,
+ tree: Tree) = {
+
+ val outerCtx = this.dup // context for generating exception handlers, covered by finalizer
+ val finalizerCtx = this.dup // context for generating finalizer handler
+ val afterCtx = outerCtx.newBlock
+
+ if (finalizer != EmptyTree) {
+ // finalizer is covers try and all catch blocks, i.e.
+ // try { try { .. } catch { ..} } finally { .. }
+ val exh = outerCtx.newHandler(NoSymbol, UNIT)
+ this.addActiveHandler(exh)
+ val ctx = finalizerCtx.enterHandler(exh)
+ if (settings.Xdce.value) ctx.bb.emit(LOAD_EXCEPTION())
+ val ctx1 = genLoad(finalizer, ctx, UNIT)
+ ctx1.bb.emit(JUMP(afterCtx.bb))
+ ctx1.bb.close
+ finalizerCtx.endHandler()
+ }
+
+ for (handler <- handlers) {
+ val exh = this.newHandler(handler._1, handler._2)
+ var ctx1 = outerCtx.enterHandler(exh)
+ if (settings.Xdce.value) ctx1.bb.emit(LOAD_EXCEPTION())
+ ctx1 = handler._3(ctx1)
+ ctx1.bb.emit(JUMP(afterCtx.bb))
+ ctx1.bb.close
+ outerCtx.endHandler()
+ }
+
+ val bodyCtx = this.newBlock
+
+ val finalCtx = body(bodyCtx)
+
+ outerCtx.bb.emit(JUMP(bodyCtx.bb))
+ outerCtx.bb.close
+
+ finalCtx.bb.emit(JUMP(afterCtx.bb))
+ finalCtx.bb.close
+
+ afterCtx
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala
index 7d9b2fd537..22d7ce90b7 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala
@@ -9,8 +9,8 @@ package scala.tools.nsc
package backend
package icode;
-import scala.tools.nsc.ast._;
-import scala.collection.mutable.{Stack, HashSet, BitSet};
+import scala.tools.nsc.ast._
+import scala.collection.mutable.{Stack, HashSet, BitSet, ListBuffer}
trait Linearizers { self: ICodes =>
import opcodes._;
@@ -201,4 +201,142 @@ trait Linearizers { self: ICodes =>
}
}
+ /** The MSIL linearizer is used only for methods with at least one exception handler.
+ * It makes sure that all the blocks belonging to a `try`, `catch` or `finally` block
+ * are emitted in an order that allows the lexical nesting of try-catch-finally, just
+ * like in the source code.
+ */
+ class MSILLinearizer extends Linearizer {
+ /** The MSIL linearizer first calls a NormalLInearizer. This is because the ILGenerator checks
+ * the stack size before emitting instructions. For instance, to emit a `store`, there needs
+ * to be some value on the stack. This can blow up in situations like this:
+ * ...
+ * jump 3
+ * 4: store_local 0
+ * jump 5
+ * 3: load_value
+ * jump 4
+ * 5: ...
+ * here, 3 must be scheduled first.
+ *
+ * The NormalLinearizer also removes dead blocks (blocks without predecessor). This is important
+ * in the following example:
+ * try { throw new Exception }
+ * catch { case e => throw e }
+ * which adds a dead block containing just a "throw" (which, again, would blow up code generation
+ * because of the stack size; there's no value on the stack when emitting that `throw`)
+ */
+ val normalLinearizer = new NormalLinearizer()
+
+ def linearize(m: IMethod): List[BasicBlock] = {
+
+ val handlersByCovered = m.exh.groupBy(_.covered)
+
+ // number of basic blocks covered by the entire try-catch expression
+ def size(covered: collection.immutable.Set[BasicBlock]) = {
+ val hs = handlersByCovered(covered)
+ covered.size + (hs :\ 0)((h, s) => h.blocks.length + s)
+ }
+
+ val tryBlocks = handlersByCovered.keysIterator.toList.sortWith(size(_) > size(_))
+
+ var result = normalLinearizer.linearize(m)
+
+ val frozen = HashSet[BasicBlock](result.head)
+ for (tryBlock <- tryBlocks) {
+ result = groupBlocks(m, result, handlersByCovered(tryBlock), frozen)
+ }
+ result
+ }
+
+ /** @param handlers a list of handlers covering the same blocks (same try, multiple catches)
+ * @param frozen blocks can't be moved (fist block of a method, blocks directly following a try-catch)
+ */
+ def groupBlocks(method: IMethod, blocks: List[BasicBlock], handlers: List[ExceptionHandler], frozen: HashSet[BasicBlock]) = {
+ assert(blocks.head == method.code.startBlock, method)
+
+ // blocks before the try, and blocks for the try
+ val beforeAndTry = new ListBuffer[BasicBlock]()
+ // blocks for the handlers
+ val catches = handlers map (_ => new ListBuffer[BasicBlock]())
+ // blocks to be put at the end
+ val after = new ListBuffer[BasicBlock]()
+
+ var beforeTry = true
+ val head = handlers.head
+
+ for (b <- blocks) {
+ if (head covers b) {
+ beforeTry = false
+ beforeAndTry += b
+ } else {
+ val handlerIndex = handlers.indexWhere(_.blocks.contains(b))
+ if (handlerIndex >= 0) {
+ catches(handlerIndex) += b
+ } else if (beforeTry) {
+ beforeAndTry += b
+ } else {
+ after += b
+ }
+ }
+ }
+
+ // reorder the blocks in "catches" so that the "firstBlock" is actually first
+ for ((lb, handler) <- catches.zip(handlers)) {
+ lb -= handler.startBlock
+ handler.startBlock +=: lb
+ }
+
+ // The first block emitted after a try-catch must be the the one that the try / catch
+ // blocks jump to (because in msil, these jumps cannot be emitted manually)
+ var firstAfter: Option[BasicBlock] = None
+
+ // Find the (hopefully) unique successor, look at the try and all catch blocks
+ var blks = head.covered.toList :: handlers.map(_.blocks)
+ while (firstAfter.isEmpty && !blks.isEmpty) {
+ val b = blks.head
+ blks = blks.tail
+
+ val leaving = leavingBlocks(b)
+ // no leaving blocks when the try or catch ends with THROW or RET
+ if (!leaving.isEmpty) {
+ assert(leaving.size <= 1, leaving)
+ firstAfter = Some(leaving.head)
+ }
+ }
+ if (firstAfter.isDefined) {
+ val b = firstAfter.get
+ if (frozen contains b) {
+ assert(after contains b, b +", "+ method)
+ } else {
+ frozen += b
+ if (beforeAndTry contains b) {
+ beforeAndTry -= b
+ } else {
+ assert(after contains b, after)
+ after -= b
+ }
+ b +=: after
+ }
+ }
+
+ for (lb <- catches) { beforeAndTry ++= lb }
+ beforeAndTry ++= after
+ beforeAndTry.toList
+ }
+
+ /** Returns all direct successors of `blocks` wich are not part
+ * that list, i.e. successors outside the `blocks` list.
+ */
+ private def leavingBlocks(blocks: List[BasicBlock]) = {
+ val res = new HashSet[BasicBlock]()
+ for (b <- blocks; s <- b.directSuccessors; if (!blocks.contains(s)))
+ res += s
+ res
+ }
+
+ def linearizeAt(m: IMethod, start: BasicBlock): List[BasicBlock] = {
+ error("not implemented")
+ }
+ }
}
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
index 6bc87d082d..2120ec79ec 100644
--- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
+++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
@@ -11,7 +11,7 @@ package backend.msil
import java.io.{File, IOException}
import java.nio.{ByteBuffer, ByteOrder}
-import scala.collection.mutable.{Map, HashMap, HashSet, Stack}
+import scala.collection.mutable.{Map, HashMap, HashSet, Stack, ListBuffer}
import scala.tools.nsc.symtab._
import scala.tools.nsc.util.Position
@@ -206,7 +206,6 @@ abstract class GenMSIL extends SubComponent {
var clasz: IClass = _
var method: IMethod = _
- var code: Code = _
var massembly: AssemblyBuilder = _
var mmodule: ModuleBuilder = _
@@ -574,646 +573,257 @@ abstract class GenMSIL extends SubComponent {
}
- var linearization: List[BasicBlock] = Nil
- // a "ret" instruction is needed (which is not present in
- // icode) if there's no code after a try-catch block
- var needAdditionalRet: Boolean = false
+ /** Special linearizer for methods with at least one exception handler. This
+ * linearizer brings all basic blocks in the right order so that nested
+ * try-catch and try-finally blocks can be emitted.
+ */
+ val msilLinearizer = new MSILLinearizer()
- def genCode(m: IMethod) {
- code = m.code
+ val labels: HashMap[BasicBlock, Label] = new HashMap()
- labels.clear
- linearization = linearizer.linearize(m)
- val orderedBlocks = (if (m.exh != Nil) orderBlocksForExh(linearization, m.exh)
- else linearization)
-
- makeLabels(orderedBlocks) // orderBlocksForExh may create new Blocks -> new Labels
- genBlocks(orderedBlocks)
- if (needAdditionalRet) {
- mcode.Emit(OpCodes.Ret)
- needAdditionalRet = false
- }
- }
+ def genCode(m: IMethod) {
- abstract class ExHInstruction(handler: ExceptionHandler) { }
- case class BeginExceptionBlock(handler: ExceptionHandler) extends ExHInstruction(handler)
- case class BeginCatchBlock(handler: ExceptionHandler, exceptionType: MsilType) extends ExHInstruction(handler)
- case class BeginFinallyBlock(handler: ExceptionHandler) extends ExHInstruction(handler)
- case class EndExceptionBlock(handler: ExceptionHandler) extends ExHInstruction(handler)
-
-
- abstract class Block {
- var closed: Boolean = false
- def parentBlockList: Option[BlockList0]
- def firstBasicBlock: BasicBlock
- def lastBasicBlock: BasicBlock
-// def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock]
- def close(): Unit
-/* protected def findExceptionBlock(list: List[Block], exh: ExceptionHandler): Option[ExceptionBlock] = {
- var res: Option[ExceptionBlock] = None
- var i: Int = 0
- while (i < list.length && res == None) {
- val b = list(i)
- val exB = b.getExceptionBlock(exh)
- exB match {
- case some: Some[ExceptionBlock] => res = some
- case None => ()
- }
- i = i + 1
- }
- res
- } */
- }
- case class CodeBlock(parent: BlockList0) extends Block {
- var basicBlocks: List[BasicBlock] = Nil
- def isEmpty = basicBlocks.isEmpty
- override def firstBasicBlock: BasicBlock = {
- if(isEmpty) null
- else {
- if (closed) basicBlocks.head
- else basicBlocks.last
- }
- }
- override def lastBasicBlock: BasicBlock = {
- if(isEmpty) null
- else {
- if (closed) basicBlocks.last
- else basicBlocks.head
- }
- }
- override def parentBlockList = Some(parent)
-// override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = None
- override def close() {
- basicBlocks = basicBlocks.reverse
- closed = true
- }
- override def toString() = {
- var res = ""
- res = res + TopBlock.indent + "CodeBlock(" + basicBlocks + ")\n"
- res
- }
- }
- abstract class BlockList0 extends Block {
- var blocks: List[Block] = Nil
- override def firstBasicBlock: BasicBlock = {
- if(blocks.isEmpty) null
- else {
- if (closed) blocks.head.firstBasicBlock
- else blocks.last.firstBasicBlock
- }
- }
- override def lastBasicBlock: BasicBlock = {
- if(blocks.isEmpty) null
- else {
- if (closed) blocks.last.lastBasicBlock
- else blocks.head.lastBasicBlock
- }
- }
-/* override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = {
- findExceptionBlock(blocks, exh)
- } */
- def addExceptionBlock(exh: ExceptionHandler) = {
- if (settings.debug.value)
- log("new exc block with " + exh + " to " + this)
- val e = new ExceptionBlock(this, exh)
- blocks = e :: blocks
- e
- }
- def addBasicBlock(bb: BasicBlock) = {
+ def makeLabels(blocks: List[BasicBlock]) = {
if (settings.debug.value)
- log("adding bb " + bb + " to " + this)
- var cb: CodeBlock = if (!blocks.isEmpty) {
- blocks.head match {
- case blk: CodeBlock => blk
- case _ => null
- }
- } else null
- if (cb == null) {
- cb = new CodeBlock(this)
- blocks = cb :: blocks
- }
- cb.basicBlocks = bb :: cb.basicBlocks
- }
- override def close() {
- blocks.foreach(_.close)
- blocks = blocks.reverse
- closed = true
- }
- override def toString() = {
- var res = ""
- res = res + TopBlock.indent + "BlockList0:\n"
- TopBlock.indent = TopBlock.indent + " "
- for (b <- blocks)
- res = res + b + "\n"
- TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-2)
- res
- }
- }
- case class BlockList(parent: Block) extends BlockList0 {
- override def parentBlockList: Option[BlockList0] = {
- if (parent == TopBlock)
- Some(TopBlock)
- else parent match {
- case bl: BlockList => Some(bl)
- case cb: CatchBlock => Some(cb)
- case _ => parent.parentBlockList
- }
- }
- override def toString() = {
- var res = ""
- res = res + TopBlock.indent + "BlockList:\n"
- res = res + super.toString()
- res
- }
- }
- case class ExceptionBlock(parent: Block, handler: ExceptionHandler) extends Block {
- var tryBlock: BlockList = new BlockList(this)
- var catchBlocks: List[CatchBlock] = Nil
- var finallyBlock: BlockList = new BlockList(this)
- override def firstBasicBlock = {
- tryBlock.firstBasicBlock
- }
- override def lastBasicBlock = {
- if (!finallyBlock.blocks.isEmpty)
- finallyBlock.lastBasicBlock
- else if(!catchBlocks.isEmpty) {
- if (closed) catchBlocks.last.lastBasicBlock
- else catchBlocks.head.lastBasicBlock
- } else {
- tryBlock.lastBasicBlock
- }
- }
- override def parentBlockList: Option[BlockList0] = {
- if (parent == TopBlock)
- Some(TopBlock)
- else parent match {
- case bl: BlockList => Some(bl)
- case cb: CatchBlock => Some(cb)
- case _ => parent.parentBlockList
- }
- }
-/* override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = {
- if (exh == handler) Some(this)
- else {
- val t = if (tryBlock == null) Nil else List(tryBlock)
- val f = if (finallyBlock == null) Nil else List(finallyBlock)
- findExceptionBlock(t ::: catchBlocks ::: f, exh)
- }
- }
-*/
- def addCatchBlock(exSym: Symbol): CatchBlock = {
- if (settings.debug.value)
- log("new catch block with " + exSym + " to " + this)
- val c = new CatchBlock(this, exSym)
- catchBlocks = c :: catchBlocks
- c
- }
- override def close() {
- tryBlock.close
- catchBlocks.foreach(_.close)
- catchBlocks = catchBlocks.reverse
- finallyBlock.close
- closed = true
- }
- override def toString() = {
- var res = ""
- res = res + TopBlock.indent + "ExceptionBlock, handler: " + handler + "\n"
- res = res + TopBlock.indent + " " + "try:\n"
- TopBlock.indent = TopBlock.indent + " "
- res = res + tryBlock + "\n"
- TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4)
- res = res + TopBlock.indent + " " + "catch:\n"
- TopBlock.indent = TopBlock.indent + " "
- for (b <- catchBlocks)
- res = res + b + "\n"
- TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4)
- res = res + TopBlock.indent + " " + "finally:\n"
- TopBlock.indent = TopBlock.indent + " "
- res = res + finallyBlock + "\n"
- TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4)
- res
- }
- }
- case class CatchBlock(parent: ExceptionBlock, exSym: Symbol) extends BlockList0 {
- override def parentBlockList: Option[BlockList0] = {
- parent.parentBlockList
- }
- override def toString() = {
- var res = ""
- res = res + TopBlock.indent + "CatchBlock:\n"
- res = res + super.toString()
- res
- }
- }
- case object TopBlock extends BlockList0 {
- var indent = ""
- override def parentBlockList = None
- override def toString() = {
- var res = ""
- res = res + TopBlock.indent + "TopBlock:\n"
- res = res + super.toString()
- res
- }
- }
-
- // for every basic block, a list of ExHInstructions to be executed:
- // - Begin_ are executed before the block
- // - EndExceptionBlock is executed after the block
- val bb2exHInstructions: HashMap[BasicBlock, List[ExHInstruction]] = new HashMap()
- // at the end of a try, catch or finally block, the jumps must not be emitted,
- // the automatically generated leave (or endfinally) will do the job.
- val omitJumpBlocks: HashSet[BasicBlock] = new HashSet()
-
- // suposes that finalizers are the same for different handlers
- // covering the same blocks
- def orderBlocksForExh(blocks: List[BasicBlock], exH: List[ExceptionHandler]): List[BasicBlock] = {
-
- def moveToFront[T](xs: List[T], x: T) = (xs indexOf x) match {
- case -1 => x :: xs
- case idx => x :: (xs take idx) ::: (xs drop (idx + 1))
- }
- def moveToEnd[T](xs: List[T], x: T) = (xs indexOf x) match {
- case -1 => xs ::: List(x)
- case idx => (xs take idx) ::: (xs drop (idx + 1)) ::: List(x)
+ log("Making labels for: " + method)
+ for (bb <- blocks) labels(bb) = mcode.DefineLabel()
}
- var blocksToPut: List[BasicBlock] = blocks
- var nextBlock: BasicBlock = null
- var untreatedHandlers: List[ExceptionHandler] = exH
- TopBlock.blocks = Nil
- var currentBlock: BlockList0 = TopBlock
- def addBlocks(b: List[BasicBlock]):Unit = b match {
- case Nil => if (settings.debug.value) log("adding " + b)
-
- case x :: xs =>
- if (settings.debug.value) log("adding " + b)
- // problem: block may already be added, and and needs to be moved.
- // if nextblock NOT in b: check if nextblock in blocksToPut, if NOT, check if movable, else don't put
- if (nextBlock != null && b.contains(nextBlock)) {
- val blocksToAdd = moveToFront(b, nextBlock)
- nextBlock = null
- addBlocks(blocksToAdd)
- }
- else if (untreatedHandlers.forall(h => !(h.covers(x)))) {
-
- if (settings.debug.value) log(" no new handler for " + x)
- if (untreatedHandlers.forall(h => !(h.blocks.contains(x) ||
- (h.finalizer != null &&
- h.finalizer.covers(x)))))
- {
- // the block is not part of some catch or finally code
- currentBlock.addBasicBlock(x)
- blocksToPut = moveToFront(blocksToPut, x)
- if (settings.debug.value) log(" -> addBlocks(" + xs + ")")
- addBlocks(xs)
- } else {
- if (settings.debug.value) log("x is part of catch or finally block")
-
- // check if the covered code of the handler x belongs to is empty
- // this check is not needed for finalizers: empty try with finalizer
- // is optimized by compiler (no try left)
- if(untreatedHandlers.forall(h =>
- (!h.blocks.contains(x) || h.covered.isEmpty))) {
- blocksToPut = moveToFront(blocksToPut, x)
- addBlocks(xs)
- } else
- addBlocks(xs ::: List(x))
- }
- } else { // there are new handlers for this block
-
- var firstBlockAfter: HashMap[ExceptionHandler,BasicBlock] = new HashMap()
- val savedCurrentBlock = currentBlock
- /**
- * the output blocks of this method are changed so that:
- * - only one block has a successor outside the set of blocks
- * - this block is the last of the reusulting list
- *
- * side-effect: it stores the successor in the hashMap
- * firstBlockAfter, which has to be emitted first after try/catch/finally,
- * because the target of the Leave-instruction will always be the first
- * instruction after EndExceptionBlock
- *
- * returns: the output blocks plus an Option containing the possibly created
- * new block
- **/
- def adaptBlocks(blocks: List[BasicBlock], exh: ExceptionHandler): (List[BasicBlock], Option[BasicBlock]) = {
- def outsideTargets(block: BasicBlock, blocks: List[BasicBlock]) = {
- /* The catch block of the ExceptionHandler is always a successor of any block inside the try
- * (see successors method in BasicBlocks.scala)
- * Thus, this successor does not correspond to a jump outside the exception handler
- * and has to be ignored when computing the list of blocks leaving the exception handler. */
- val res = block.successors.filter(scc => !blocks.contains(scc) && scc != exh.startBlock)
- if (settings.debug.value) log("outside of " + block + " = " + res + " succ " + block.successors)
- res
- }
- // get leaving blocks and their outside targets
- def leavingBlocks(blocks: List[BasicBlock]): List[(BasicBlock, List[BasicBlock])] = {
- for {b <- blocks
- val t = outsideTargets(b, blocks)
- if t.length != 0 } yield (b, t)
- }
+ labels.clear
- def replaceOutJumps(blocks: List[BasicBlock], leaving: List[(BasicBlock, List[BasicBlock])], exh: ExceptionHandler): (List[BasicBlock], Option[BasicBlock]) = {
- def replaceJump(block: BasicBlock, from: BasicBlock, to: BasicBlock) = block.lastInstruction match {
- case JUMP(whereto) =>
- //assert(from == whereto)
- block.replaceInstruction(block.lastInstruction, JUMP(to))
- case CJUMP(success, failure, cond, kind) =>
- if (from == success)
- block.replaceInstruction(block.lastInstruction, CJUMP(to, failure, cond, kind))
- else
- //assert(from == failure)
- if (from == failure)
- block.replaceInstruction(block.lastInstruction, CJUMP(success, to, cond, kind))
- case CZJUMP(success, failure, cond, kind) =>
- if (from == success)
- block.replaceInstruction(block.lastInstruction, CZJUMP(to, failure, cond, kind))
- else
- //assert(from == failure)
- if (from == failure)
- block.replaceInstruction(block.lastInstruction, CZJUMP(success, to, cond, kind))
- case SWITCH(tags, labels) => // labels: List[BasicBlock]
- val newLabels = labels.map(b => if (b == from) to else b)
- assert(newLabels.contains(to))
- block.replaceInstruction(block.lastInstruction, SWITCH(tags, newLabels))
- /*
- case RETURN(kind) =>
- if (kind != UNIT) {
- returnVal
- }
- block.replaceInstruction(block.lastInstructionm JUMP(to))
- */
- case _ => () //abort("expected branch at the end of block " + block)
- }
+ var linearization = if(m.exh != Nil) msilLinearizer.linearize(m)
+ else linearizer.linearize(m)
- val jumpOutBlock = blocks.last.code.newBlock
- jumpOutBlock.emit(JUMP(firstBlockAfter(exh)))
- jumpOutBlock.close
- leaving.foreach(p => {
- val lBlock = p._1
- val target = p._2(0) // the elemets of p._2 are all the same, checked before
- replaceJump(lBlock, target, jumpOutBlock)
- if (settings.debug.value) log("replacing " + lBlock + " target " + target + " jump out " + jumpOutBlock)
- })
- (blocks ::: List(jumpOutBlock), Some(jumpOutBlock))
- }
+ if (m.exh != Nil)
+ linearization = computeExceptionMaps(linearization, m)
- val leaving = leavingBlocks(blocks)
- if (settings.debug.value) log("leaving " + leaving)
- if (leaving.length == 0)
- (blocks, None)
- else if (leaving.length == 1) {
- val outside = leaving(0)._2
- //assert(outside.forall(b => b == outside(0)), "exception-block leaving to multiple targets")
- if (!firstBlockAfter.isDefinedAt(exh))
- firstBlockAfter(exh) = outside(0)
- //else ()
- //assert(firstBlockAfter(exh) == outside(0), "try/catch leaving to multiple targets: " + firstBlockAfter(exh) + ", new: " + outside(0))
-
- val last = leaving(0)._1
- (moveToEnd(blocks, last), None)
- } else {
- val outside = leaving.flatMap(p => p._2)
- //assert(outside.forall(b => b == outside(0)), "exception-block leaving to multiple targets")
- if (!firstBlockAfter.isDefinedAt(exh))
- firstBlockAfter(exh) = outside(0)
- //else
- //assert(firstBlockAfter(exh) == outside(0), "try/catch leaving to multiple targets")
- replaceOutJumps(blocks, leaving, exh)
- }
- }
+ makeLabels(linearization)
- var affectedHandlers: List[ExceptionHandler] = Nil
- untreatedHandlers.foreach( (h) => {
- if (h.covers(x)) {
- affectedHandlers = h :: affectedHandlers
- }
- })
-
- // shorter try-catch-finally last (the ones contained in another)
- affectedHandlers = affectedHandlers.sortWith(_.covered.size > _.covered.size)
- affectedHandlers = affectedHandlers.filter(h => {h.covered.size == affectedHandlers(0).covered.size})
- untreatedHandlers = untreatedHandlers filterNot (affectedHandlers contains)
-
- // more than one catch produces more than one exh, but we only need one
- var singleAffectedHandler: ExceptionHandler = affectedHandlers(0) // List[ExceptionHandler] = Nil
- var exceptionBlock: Option[ExceptionBlock] = None
- if (settings.debug.value) log("affected handlers " + affectedHandlers)
- affectedHandlers.foreach(h1 => {
- val (adaptedBlocks, newBlock) = adaptBlocks(blocksToPut.intersect(h1.blocks), singleAffectedHandler)
- newBlock match {
- case Some(block) =>
- blocksToPut = blocksToPut ::: List(block)
- h1.addBlock(block)
- case None => ()
- }
- val orderedCatchBlocks = moveToFront(adaptedBlocks, h1.startBlock)
-
- exceptionBlock match {
- case Some(excBlock) =>
- val catchBlock = excBlock.addCatchBlock(h1.cls)
- currentBlock = catchBlock
- addBlocks(orderedCatchBlocks)
- case None =>
- val excBlock = currentBlock.addExceptionBlock(singleAffectedHandler)
- exceptionBlock = Some(excBlock)
-
- val (tryBlocks, newBlock) = adaptBlocks(blocksToPut.intersect(singleAffectedHandler.covered.toList), singleAffectedHandler)
-
- newBlock match {
- case Some(block) =>
- blocksToPut = blocksToPut ::: List(block)
- singleAffectedHandler.addCoveredBlock(block)
- case None => ()
- }
- currentBlock = excBlock.tryBlock
- if (settings.debug.value) log("adding try blocks " + tryBlocks)
- addBlocks(tryBlocks)
-
- if (singleAffectedHandler.finalizer != null && singleAffectedHandler.finalizer != NoFinalizer) {
- val (blocks0, newBlock) = adaptBlocks(blocksToPut.intersect(singleAffectedHandler.finalizer.blocks), singleAffectedHandler)
- newBlock match {
- case Some(block) =>
- blocksToPut = blocksToPut ::: List(block)
- singleAffectedHandler.finalizer.addBlock(block)
- case None => ()
- }
- val blocks = moveToFront(blocks0, singleAffectedHandler.finalizer.startBlock)
- currentBlock = excBlock.finallyBlock
- addBlocks(blocks)
- }
-
- val catchBlock = excBlock.addCatchBlock(singleAffectedHandler.cls)
- currentBlock = catchBlock
- addBlocks(orderedCatchBlocks)
- }
- if (firstBlockAfter.isDefinedAt(singleAffectedHandler))
- nextBlock = firstBlockAfter(singleAffectedHandler)
- else
- nextBlock = null
- })
+ genBlocks(linearization)
- currentBlock = savedCurrentBlock
+ beginExBlock.clear()
+ beginCatchBlock.clear()
+ endExBlock.clear()
+ omitJumpBlocks.clear()
+ }
- if (settings.debug.value)
- log(" -> addBlocks(" + xs.intersect(blocksToPut) + ")")
- addBlocks(xs.intersect(blocksToPut))
- }
+ def genBlocks(blocks: List[BasicBlock], previous: BasicBlock = null) {
+ blocks match {
+ case Nil => ()
+ case x :: Nil => genBlock(x, prev = previous, next = null)
+ case x :: y :: ys => genBlock(x, prev = previous, next = y); genBlocks(y :: ys, previous = x)
}
+ }
- // begin method orderBlocksForExh
-
- if (settings.debug.value)
- log("before: " + blocks)
- // some blocks may have been removed by linearization
- untreatedHandlers.foreach(h => {
- h.blocks = h.blocks.intersect(blocksToPut)
- h.covered = h.covered.intersect(collection.immutable.HashSet.empty ++ blocksToPut)
- if (h.finalizer != null && h.finalizer != NoFinalizer)
- h.finalizer.blocks = h.finalizer.blocks.intersect(blocksToPut)
- })
- addBlocks(blocks)
-
- TopBlock.close()
+ // the try blocks starting at a certain BasicBlock
+ val beginExBlock = new HashMap[BasicBlock, List[ExceptionHandler]]()
- if (settings.debug.value) log("TopBlock tree is: ")
- if (settings.debug.value) log(TopBlock)
+ // the catch blocks starting / endling at a certain BasicBlock
+ val beginCatchBlock = new HashMap[BasicBlock, ExceptionHandler]()
+ val endExBlock = new HashMap[BasicBlock, List[ExceptionHandler]]()
- bb2exHInstructions.clear
- def addExHInstruction(b: BasicBlock, ehi: ExHInstruction) = {
- if (settings.debug.value)
- log("adding exhinstr: " + b + " -> " + ehi)
+ // at the end of a try or catch block, the jumps must not be emitted.
+ // the automatically generated leave will do the job.
+ val omitJumpBlocks: HashSet[BasicBlock] = new HashSet()
- if (bb2exHInstructions.contains(b)) {
- bb2exHInstructions(b) = ehi :: bb2exHInstructions(b)
- } else {
- bb2exHInstructions(b) = List(ehi)
- }
- }
- omitJumpBlocks.clear
- def omitJump(blk: BasicBlock) = {
- omitJumpBlocks += blk
- }
- var orderedBlocks: List[BasicBlock] = Nil
- def flatten(block: Block) {
- if (block == TopBlock) {
- for (b <- TopBlock.blocks) flatten(b)
- } else block match {
- case cb: CodeBlock =>
- orderedBlocks = orderedBlocks ::: cb.basicBlocks
- case bl: BlockList =>
- for (b <- bl.blocks) flatten(b)
- case cb: CatchBlock =>
- for (b <- cb.blocks) flatten(b)
- case eb: ExceptionBlock =>
- val handler = eb.handler
- if (settings.debug.value) {
- log("new exception block " + eb)
- log("try: " + eb.tryBlock)
- }
- addExHInstruction(eb.tryBlock.firstBasicBlock, new BeginExceptionBlock(handler))
- omitJump(eb.tryBlock.lastBasicBlock)
- flatten(eb.tryBlock)
- for (c <- eb.catchBlocks) {
- val t: MsilType = (if (c.exSym == NoSymbol) EXCEPTION
- else getType(c.exSym))
- addExHInstruction(c.firstBasicBlock, new BeginCatchBlock(handler, t))
- omitJump(c.lastBasicBlock)
- flatten(c)
- }
- if (handler.finalizer != null && handler.finalizer != NoFinalizer) {
- addExHInstruction(eb.finallyBlock.firstBasicBlock, new BeginFinallyBlock(handler))
- flatten(eb.finallyBlock)
- addExHInstruction(eb.finallyBlock.lastBasicBlock, new EndExceptionBlock(handler))
- omitJump(eb.finallyBlock.lastBasicBlock)
- } else {
- addExHInstruction(eb.catchBlocks.last.lastBasicBlock, new EndExceptionBlock(handler))
+ /** Computes which blocks are the beginning / end of a try or catch block */
+ private def computeExceptionMaps(blocks: List[BasicBlock], m: IMethod): List[BasicBlock] = {
+ val visitedBlocks = new HashSet[BasicBlock]()
+
+ // handlers which have not been intruduced so far
+ var openHandlers = m.exh
+
+
+ /** Example
+ * try {
+ * try {
+ * // *1*
+ * } catch {
+ * case h1 =>
+ * }
+ * } catch {
+ * case h2 =>
+ * case h3 =>
+ * try {
+ *
+ * } catch {
+ * case h4 => // *2*
+ * case h5 =>
+ * }
+ * }
+ */
+
+ // Stack of nested try blocks. Each bloc has a List of ExceptionHandler (multiple
+ // catch statements). Example *1*: Stack(List(h2, h3), List(h1))
+ val currentTryHandlers = new Stack[List[ExceptionHandler]]()
+
+ // Stack of nested catch blocks. The head of the list is the current catch block. The
+ // tail is all following catch blocks. Example *2*: Stack(List(h3), List(h4, h5))
+ val currentCatchHandlers = new Stack[List[ExceptionHandler]]()
+
+ var prev: BasicBlock = null
+
+ for (b <- blocks) {
+
+ // are we past the current catch blocks?
+ def endHandlers(): List[ExceptionHandler] = {
+ var res: List[ExceptionHandler] = Nil
+ if (!currentCatchHandlers.isEmpty) {
+ val handler = currentCatchHandlers.top.head
+ if (!handler.blocks.contains(b)) {
+ // all blocks of the handler are either visited, or not part of the linearization (i.e. dead)
+ assert(handler.blocks.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)),
+ "Bad linearization of basic blocks inside catch. Found block not part of the handler\n"+
+ b.fullString +"\nwhile in catch-part of\n"+ handler)
+
+ omitJumpBlocks += prev
+
+ val rest = currentCatchHandlers.pop.tail
+ if (rest.isEmpty) {
+ res = handler :: endHandlers()
+ } else {
+ currentCatchHandlers.push(rest)
+ beginCatchBlock(b) = rest.head
+ }
}
+ }
+ res
+ }
+ val end = endHandlers()
+ if (!end.isEmpty) endExBlock(b) = end
+
+ // are we past the current try block?
+ if (!currentTryHandlers.isEmpty) {
+ val handler = currentTryHandlers.top.head
+ if (!handler.covers(b)) {
+ // all of the covered blocks are visited, or not part of the linearization
+ assert(handler.covered.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)),
+ "Bad linearization of basic blocks inside try. Found non-covered block\n"+
+ b.fullString +"\nwhile in try-part of\n"+ handler)
+
+ assert(handler.startBlock == b,
+ "Bad linearization of basic blocks. The entry block of a catch does not directly follow the try\n"+
+ b.fullString +"\n"+ handler)
+
+ val handlers = currentTryHandlers.pop
+ currentCatchHandlers.push(handlers)
+ beginCatchBlock(b) = handler
+ omitJumpBlocks += prev
+ }
}
- }
-
- flatten(TopBlock)
-
- assert(untreatedHandlers.forall((h) => h.covered.isEmpty),
- "untreated exception handlers left: " + untreatedHandlers)
- // remove catch blocks from empty handlers (finally-blocks remain)
- untreatedHandlers.foreach((h) => {
- orderedBlocks = orderedBlocks filterNot (h.blocks contains)
- })
-
- // take care of order in which exHInstructions are executed (BeginExceptionBlock as last)
- bb2exHInstructions.keysIterator.foreach((b) => {
- bb2exHInstructions(b).sortBy(x => x.isInstanceOf[BeginExceptionBlock])
- })
- if (settings.debug.value) {
- log("after: " + orderedBlocks)
- log(" exhInstr: " + bb2exHInstructions)
+ // are there try blocks starting at b?
+ val (newHandlers, stillOpen) = openHandlers.partition(_.covers(b))
+ openHandlers = stillOpen
+
+ val newHandlersBySize = newHandlers.groupBy(_.covered.size)
+ // big handlers first, smaller ones are nested inside the try of the big one
+ // (checked by the assertions below)
+ val sizes = newHandlersBySize.keysIterator.toList.sortWith(_ > _)
+
+ val beginHandlers = new ListBuffer[ExceptionHandler]
+ for (s <- sizes) {
+ val sHandlers = newHandlersBySize(s)
+ for (h <- sHandlers) {
+ assert(h.covered == sHandlers.head.covered,
+ "bad nesting of exception handlers. same size, but not covering same blocks\n"+
+ h +"\n"+ sHandlers.head)
+ assert(h.resultKind == sHandlers.head.resultKind,
+ "bad nesting of exception handlers. same size, but the same resultKind\n"+
+ h +"\n"+ sHandlers.head)
+ }
+ for (bigger <- beginHandlers; h <- sHandlers) {
+ assert(h.covered.subsetOf(bigger.covered),
+ "bad nesting of exception handlers. try blocks of smaller handler are not nested in bigger one.\n"+
+ h +"\n"+ bigger)
+ assert(h.blocks.toSet.subsetOf(bigger.covered),
+ "bad nesting of exception handlers. catch blocks of smaller handler are not nested in bigger one.\n"+
+ h +"\n"+ bigger)
+ }
+ beginHandlers += sHandlers.head
+ currentTryHandlers.push(sHandlers)
+ }
+ beginExBlock(b) = beginHandlers.toList
+ visitedBlocks += b
+ prev = b
+ }
+
+ // if there handlers left (i.e. handlers covering nothing, or a
+ // non-existent (dead) block), remove their catch-blocks.
+ val liveBlocks = if (openHandlers.isEmpty) blocks else {
+ blocks.filter(b => openHandlers.forall(h => !h.blocks.contains(b)))
+ }
+
+ /** There might be open handlers, but no more blocks. happens when try/catch end
+ * with `throw` or `return`
+ * def foo { try { .. throw } catch { _ => .. throw } }
+ *
+ * In this case we need some code after the catch block for the auto-generated
+ * `leave` instruction. So we're adding a (dead) `throw new Exception`.
+ */
+ val rest = currentCatchHandlers.map(handlers => {
+ assert(handlers.length == 1, handlers)
+ handlers.head
+ }).toList
+
+ if (rest.isEmpty) {
+ liveBlocks
+ } else {
+ omitJumpBlocks += prev
+ val b = m.code.newBlock
+ b.emit(Seq(
+ NEW(REFERENCE(definitions.ThrowableClass)),
+ DUP(REFERENCE(definitions.ObjectClass)),
+ CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)),
+ THROW()
+ ))
+ b.close
+ endExBlock(b) = rest
+ liveBlocks ::: List(b)
}
-
- orderedBlocks
}
- var currentBlock: BasicBlock = _
- var lastBlock: BasicBlock = _
- var nextBlock: BasicBlock = _
+ /**
+ * @param block the BasicBlock to emit code for
+ * @param next the following BasicBlock, `null` if `block` is the last one
+ */
+ def genBlock(block: BasicBlock, prev: BasicBlock, next: BasicBlock) {
+ /** Creating objects works differently on .NET. On the JVM
+ * - NEW(type) => reference on Stack
+ * - DUP, load arguments, CALL_METHOD(constructor)
+ *
+ * On .NET, the NEW and DUP are ignored, but we emit a special method call
+ * - load arguments
+ * - NewObj(constructor) => reference on stack
+ *
+ * This variable tells wether the previous instruction was a NEW,
+ * we expect a DUP which is not emitted. */
+ var previousWasNEW = false
- def genBlocks(l: List[BasicBlock]) {
- l match {
- case Nil => ()
- case x :: Nil => currentBlock = x; nextBlock = null; genBlock(x)
- case x :: y :: ys => currentBlock = x; nextBlock = y; genBlock(x); genBlocks(y :: ys)
- }
- }
+ var lastLineNr: Int = 0
- var ignoreNextDup: Boolean = false
- val excResultLocals: Stack[LocalBuilder] = new Stack()
+ mcode.MarkLabel(labels(block))
- def genBlock(b: BasicBlock) {
- // at begin of the first block, there's nothing to save =>
- // lastBlock != null is secure
- def saveResult(resType: MsilType) = if (resType != MVOID && lastBlock != null) {
- lastBlock.lastInstruction match {
- case THROW() => ()
- case _ =>
- val lb: LocalBuilder = excResultLocals.top
- mcode.Emit(OpCodes.Stloc, lb)
+ if (settings.debug.value)
+ log("Generating code for block: " + block)
+
+ for (handlers <- endExBlock.get(block); exh <- handlers) {
+ mcode.EndExceptionBlock()
+ }
+ for (handler <- beginCatchBlock.get(block)) {
+ if (handler.cls == NoSymbol) {
+ // `finally` blocks are represented the same as `catch`, but with no catch-type
+ mcode.BeginFinallyBlock()
+ } else {
+ val t = getType(handler.cls)
+ mcode.BeginCatchBlock(t)
}
}
-
- if (bb2exHInstructions.contains(b)) {
- bb2exHInstructions(b).foreach((i) => i match {
- case BeginExceptionBlock(handler) =>
- if (settings.debug.value) log("begin ex blk: " + handler)
- mcode.BeginExceptionBlock()
- val resType = msilType(handler.resultKind)
- if (resType != MVOID) {
- val l = mcode.DeclareLocal(resType)
- l.SetLocalSymInfo("$exhResult")
- excResultLocals.push(l)
- }
- case BeginCatchBlock(handler, exType) =>
- if (settings.debug.value) log("begin catch blk: " + handler + ", tpe: " + exType)
- saveResult(msilType(handler.resultKind))
- mcode.BeginCatchBlock(exType)
- case BeginFinallyBlock(handler) =>
- saveResult(msilType(handler.resultKind))
- mcode.BeginFinallyBlock()
- case EndExceptionBlock(handler) => ()
- case _ => abort("unknown case: " + i)
- })
+ for (handlers <- beginExBlock.get(block); exh <- handlers) {
+ mcode.BeginExceptionBlock()
}
- mcode.MarkLabel(labels(b))
- if (settings.debug.value)
- log("Generating code for block: " + b)
-
- var lastLineNr: Int = 0
-
- for (instr <- b) {
-
- needAdditionalRet = false
-
+ for (instr <- block) {
val currentLineNr = try {
instr.pos.line
} catch {
@@ -1227,6 +837,9 @@ abstract class GenMSIL extends SubComponent {
lastLineNr = currentLineNr
}
+ if (previousWasNEW)
+ assert(instr.isInstanceOf[DUP], block)
+
instr match {
case THIS(clasz) =>
mcode.Emit(OpCodes.Ldarg_0)
@@ -1273,21 +886,18 @@ abstract class GenMSIL extends SubComponent {
case LOAD_LOCAL(local) =>
if (settings.debug.value)
log("load_local for " + local)
- val isArg: Boolean = local.arg
+ val isArg = local.arg
val i = local.index
- if (isArg) {
+ if (isArg)
loadArg(mcode)(i)
- }
- else {
+ else
loadLocal(i, local, mcode)
- }
case LOAD_FIELD(field, isStatic) =>
if (settings.debug.value)
log("LOAD_FIELD with owner: " + field.owner +
" flags: " + Flags.flagsToString(field.owner.flags))
-
- var fieldInfo: FieldInfo = fields.get(field) match {
+ var fieldInfo = fields.get(field) match {
case Some(fInfo) => fInfo
case None =>
val fInfo = getType(field.owner).GetField(msilName(field))
@@ -1296,7 +906,6 @@ abstract class GenMSIL extends SubComponent {
}
mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo)
-
case LOAD_MODULE(module) =>
if (settings.debug.value)
log("Generating LOAD_MODULE for: " + showsym(module))
@@ -1318,7 +927,7 @@ abstract class GenMSIL extends SubComponent {
}
case STORE_LOCAL(local) =>
- val isArg: Boolean = local.arg
+ val isArg = local.arg
val i = local.index
if (settings.debug.value)
log("store_local for " + local + ", index " + i)
@@ -1350,7 +959,7 @@ abstract class GenMSIL extends SubComponent {
mcode.Emit(OpCodes.Starg_S, 0)
case STORE_FIELD(field, isStatic) =>
- val fieldInfo: FieldInfo = fields.get(field) match {
+ val fieldInfo = fields.get(field) match {
case Some(fInfo) => fInfo
case None =>
val fInfo = getType(field.owner).GetField(msilName(field))
@@ -1359,11 +968,9 @@ abstract class GenMSIL extends SubComponent {
}
mcode.Emit(if (isStatic) OpCodes.Stsfld else OpCodes.Stfld, fieldInfo)
-
case CALL_PRIMITIVE(primitive) =>
genPrimitive(primitive, instr.pos)
-
case CALL_METHOD(msym, style) =>
if (msym.isClassConstructor) {
val constructorInfo: ConstructorInfo = getConstructor(msym)
@@ -1371,6 +978,12 @@ abstract class GenMSIL extends SubComponent {
// normal constructor calls are Static..
case Static(_) =>
if (method.symbol.isClassConstructor && method.symbol.owner == msym.owner)
+ // we're generating a constructor (method: IMethod is a constructor), and we're
+ // calling another constructor of the same class.
+
+ // @LUC TODO: this can probably break, namely when having: class A { def this { new A() } }
+ // instead, we should instruct the CALL_METHOD with additional information, know whether it's
+ // an instance creation constructor call or not.
mcode.Emit(OpCodes.Call, constructorInfo)
else
mcode.Emit(OpCodes.Newobj, constructorInfo)
@@ -1398,7 +1011,7 @@ abstract class GenMSIL extends SubComponent {
mcode.Emit(OpCodes.Add) // compute length (-start + end)
}
- var doEmit: Boolean = true
+ var doEmit = true
types.get(msym.owner) match {
case Some(typ) if (typ.IsEnum) => {
def negBool = {
@@ -1481,12 +1094,13 @@ abstract class GenMSIL extends SubComponent {
}
}
- case BOX(boxType) => emitBox(mcode, boxType) //mcode.Emit(OpCodes.Box, msilType(boxType))
+ case BOX(boxType) => emitBox(mcode, boxType)
case UNBOX(boxType) => emitUnbox(mcode, boxType)
case NEW(REFERENCE(cls)) =>
- ignoreNextDup = true
+ // the next instruction must be a DUP, see comment on `var previousWasNEW`
+ previousWasNEW = true
// works also for arrays and reference-types
case CREATE_ARRAY(elem, dims) =>
@@ -1502,7 +1116,6 @@ abstract class GenMSIL extends SubComponent {
mcode.Emit(OpCodes.Ldc_I4_0)
mcode.Emit(OpCodes.Ceq)
-
// works for arrays and reference-types
// part from the scala reference: "S <: T does not imply
// Array[S] <: Array[T] in Scala. However, it is possible
@@ -1510,7 +1123,6 @@ abstract class GenMSIL extends SubComponent {
// is permitted in the host environment."
case CHECK_CAST(tpe) => mcode.Emit(OpCodes.Castclass, msilType(tpe))
-
// no SWITCH is generated when there's
// - a default case ("case _ => ...") in the matching expr
// - OR is used ("case 1 | 2 => ...")
@@ -1527,7 +1139,7 @@ abstract class GenMSIL extends SubComponent {
switchLocal.SetLocalSymInfo("$switch_var")
mcode.Emit(OpCodes.Stloc, switchLocal)
- var i: Int = 0
+ var i = 0
for (l <- tags) {
var targetLabel = labels(branches(i))
for (i <- l) {
@@ -1538,25 +1150,24 @@ abstract class GenMSIL extends SubComponent {
i += 1
}
val defaultTarget = labels(branches(i))
- if (nextBlock != defaultTarget && !omitJumpBlocks.contains(currentBlock))
+ if (next != defaultTarget && !omitJumpBlocks.contains(block))
mcode.Emit(OpCodes.Br, defaultTarget)
case JUMP(whereto) =>
- if (nextBlock != whereto && !omitJumpBlocks.contains(currentBlock))
+ if (next != whereto && !omitJumpBlocks.contains(block))
mcode.Emit(OpCodes.Br, labels(whereto))
-
case CJUMP(success, failure, cond, kind) =>
// cond is TestOp (see Primitives.scala), and can take
// values EQ, NE, LT, GE LE, GT
// kind is TypeKind
val isFloat = kind == FLOAT || kind == DOUBLE
- if (nextBlock == success || omitJumpBlocks.contains(currentBlock)) {
+ if (next == success || omitJumpBlocks.contains(block)) {
emitBr(cond.negate, labels(failure), isFloat)
} else {
emitBr(cond, labels(success), isFloat)
- if (nextBlock != failure && !omitJumpBlocks.contains(currentBlock)) {
+ if (next != failure && !omitJumpBlocks.contains(block)) {
mcode.Emit(OpCodes.Br, labels(failure))
}
}
@@ -1564,11 +1175,11 @@ abstract class GenMSIL extends SubComponent {
case CZJUMP(success, failure, cond, kind) =>
(kind: @unchecked) match {
case BOOL | REFERENCE(_) =>
- if (nextBlock == success || omitJumpBlocks.contains(currentBlock)) {
+ if (next == success || omitJumpBlocks.contains(block)) {
emitBrBool(cond.negate, labels(failure))
} else {
emitBrBool(cond, labels(success))
- if (nextBlock != failure && !omitJumpBlocks.contains(currentBlock)) {
+ if (next != failure && !omitJumpBlocks.contains(block)) {
mcode.Emit(OpCodes.Br, labels(failure))
}
}
@@ -1584,12 +1195,11 @@ abstract class GenMSIL extends SubComponent {
mcode.Emit(OpCodes.Pop)
case DUP(kind) =>
- // needed to create new instances
- if (!ignoreNextDup) {
+ // see comment on `var previousWasNEW`
+ if (!previousWasNEW)
mcode.Emit(OpCodes.Dup)
- } else {
- ignoreNextDup = false
- }
+ else
+ previousWasNEW = false
case MONITOR_ENTER() =>
mcode.Emit(OpCodes.Call, MMONITOR_ENTER)
@@ -1597,35 +1207,12 @@ abstract class GenMSIL extends SubComponent {
case MONITOR_EXIT() =>
mcode.Emit(OpCodes.Call, MMONITOR_EXIT)
- case SCOPE_ENTER(_) | SCOPE_EXIT(_) =>
+ case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION() =>
()
}
} // end for (instr <- b) { .. }
-
- lastBlock = b // this way, saveResult knows lastBlock
-
- if (bb2exHInstructions.contains(b)) {
- bb2exHInstructions(b).foreach((i) => i match {
- case BeginExceptionBlock(handler) => ()
- case BeginCatchBlock(handler, exType) => ()
- case BeginFinallyBlock(handler) => ()
- case EndExceptionBlock(handler) =>
- if (settings.debug.value) log("end ex blk: " + handler)
- val resType = msilType(handler.resultKind)
- if (handler.finalizer == null || handler.finalizer == NoFinalizer)
- saveResult(resType)
- mcode.EndExceptionBlock()
- if (resType != MVOID) {
- val lb: LocalBuilder = excResultLocals.pop
- mcode.Emit(OpCodes.Ldloc, lb)
- } else
- needAdditionalRet = true
- case _ => abort("unknown case: " + i)
- })
- }
-
- } // end genBlock
+ }
def genPrimitive(primitive: Primitive, pos: Position) {
primitive match {
@@ -1751,8 +1338,6 @@ abstract class GenMSIL extends SubComponent {
////////////////////// labels ///////////////////////
- val labels: HashMap[BasicBlock, Label] = new HashMap() // labels for branches
-
def emitBr(condition: TestOp, dest: Label, isFloat: Boolean) {
condition match {
case EQ => mcode.Emit(OpCodes.Beq, dest)
@@ -1775,12 +1360,6 @@ abstract class GenMSIL extends SubComponent {
}
}
- def makeLabels(bs: List[BasicBlock]) {
- if (settings.debug.value)
- log("Making labels for: " + method)
- for (bb <- bs) labels(bb) = mcode.DefineLabel()
- }
-
////////////////////// local vars ///////////////////////
/**
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index fa436a4305..ce58755bdd 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -649,9 +649,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* The result will be a tree of the form
* {
* if ((bitmap$n & MASK) == 0) {
- * synhronized(this) {
+ * synchronized(this) {
* if ((bitmap$n & MASK) == 0) {
- * synhronized(this) {
+ * synchronized(this) {
* init // l$ = <rhs>
* }
* bitmap$n = bimap$n | MASK
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala
index bf8eade0cd..ea93d6c606 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala
@@ -381,7 +381,9 @@ import ILGenerator._
* (MSIL) instruction stream.
*/
def BeginFinallyBlock() {
- Emit(OpCodes.Leave, excStack.peekLabel())
+ val endExc: Label = excStack.popLabel()
+ Emit(OpCodes.Leave, endExc)
+ excStack.push(Label.Finally, endExc)
emitSpecialLabel(Label.Finally)
}
@@ -482,9 +484,7 @@ import ILGenerator._
// compute new lastLabel (next label)
val stackSize: Int = lastLabel.getStacksize() + overridePOPUSH
if (stackSize < 0) {
- throw new RuntimeException
- //System.err.println
- ("ILGenerator.emit(): Stack underflow in method: " + owner)
+ throw new RuntimeException("ILGenerator.emit(): Stack underflow in method: " + owner)
}
if (stackSize > maxstack)
maxstack = stackSize