diff options
Diffstat (limited to 'src')
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 |