diff options
10 files changed, 370 insertions, 129 deletions
diff --git a/sources/scala/tools/nsc/backend/WorklistAlgorithm.scala b/sources/scala/tools/nsc/backend/WorklistAlgorithm.scala index d69f4929ed..6676c820bc 100644 --- a/sources/scala/tools/nsc/backend/WorklistAlgorithm.scala +++ b/sources/scala/tools/nsc/backend/WorklistAlgorithm.scala @@ -8,12 +8,12 @@ package scala.tools.nsc.backend; import scala.tools.nsc.ast._; -import scala.collection.mutable.Queue; +import scala.collection.mutable.MutableList; /** * Simple implementation of a worklist algorithm. A processing * function is applied repeatedly to the first element in the - * worklist queue, as long as the queue is not empty. + * worklist, as long as the stack is not empty. * * The client class should mix-in this trait and initialize the * worklist field and define the processElement method. Then call @@ -24,8 +24,9 @@ import scala.collection.mutable.Queue; */ trait WorklistAlgorithm { type Elem; + type WList <: MutableList[Elem]; - val worklist: Queue[Elem]; + val worklist: WList; /** * Run the iterative algorithm until the worklist @@ -35,12 +36,15 @@ trait WorklistAlgorithm { def run(initWorklist: => Unit) = { initWorklist; - while (!worklist.isEmpty) - processElement(worklist.dequeue); + while (!(worklist.length == 0)) + processElement(dequeue); } /** * Process the current element from the worklist. */ def processElement(e: Elem): Unit; + + /** Remove and return the first element to be processed from the worklist. */ + def dequeue: Elem; } diff --git a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala index b0a4cb6eae..a77d195ca0 100644 --- a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -136,9 +136,10 @@ trait BasicBlocks: ICodes { } def emit(instr: Instruction, pos: Int) = { - assert (!closed || ignore, "BasicBlock closed"); + assert (!closed || ignore, "BasicBlock " + label + " closed"); if (!ignore) { +// Console.println("block " + label + ": " + instr); instr.pos = pos; instructionList = instr :: instructionList; _lastInstruction = instr; @@ -150,6 +151,9 @@ trait BasicBlocks: ICodes { assert(instructionList.length > 0, "Empty block."); closed = true; + +// Console.println("block " + label + " <closed>"); + instrs = toInstructionArray(instructionList.reverse); } diff --git a/sources/scala/tools/nsc/backend/icode/Checkers.scala b/sources/scala/tools/nsc/backend/icode/Checkers.scala index bda1e4d7fa..fdd8912d84 100644 --- a/sources/scala/tools/nsc/backend/icode/Checkers.scala +++ b/sources/scala/tools/nsc/backend/icode/Checkers.scala @@ -55,6 +55,7 @@ abstract class Checkers { val STRING = REFERENCE(definitions.StringClass); val SCALA_ALL = REFERENCE(definitions.AllClass); + val SCALA_ALL_REF = REFERENCE(definitions.AllRefClass); def checkICodes: Unit = { Console.println("[[consistency check at beginning of phase " + globalPhase.name + "]]"); @@ -299,7 +300,7 @@ abstract class Checkers { case Pair(value, obj) => checkField(obj, field); val fieldType = toTypeKind(field.tpe); - if (!(value <:< fieldType)) + if (fieldType != SCALA_ALL_REF && !(value <:< fieldType)) typeError(fieldType, value); } } diff --git a/sources/scala/tools/nsc/backend/icode/ExceptionHandlers.scala b/sources/scala/tools/nsc/backend/icode/ExceptionHandlers.scala index 86cba32ccb..126e20ebef 100644 --- a/sources/scala/tools/nsc/backend/icode/ExceptionHandlers.scala +++ b/sources/scala/tools/nsc/backend/icode/ExceptionHandlers.scala @@ -23,6 +23,7 @@ trait ExceptionHandlers: ICodes { class ExceptionHandler(val method: IMethod, label: String, val cls: Symbol) { private var coveredBlocks: List[BasicBlock] = Nil; private var _startBlock: BasicBlock = _; + var finalizer: Finalizer = _; def setStartBlock(b: BasicBlock) = _startBlock = b; def startBlock = _startBlock; @@ -40,4 +41,9 @@ trait ExceptionHandlers: ICodes { class Finalizer(method: IMethod, label: String) extends ExceptionHandler(method, label, NoSymbol) { override def toString() = "finalizer_" + label; } + + object NoFinalizer extends Finalizer(null, "<no finalizer>") { + override def startBlock: BasicBlock = error("NoFinalizer cannot have a start block."); + override def setStartBlock(b: BasicBlock): Unit = error("NoFinalizer cannot have a start block."); + } } diff --git a/sources/scala/tools/nsc/backend/icode/GenICode.scala b/sources/scala/tools/nsc/backend/icode/GenICode.scala index 9b58cf4e27..009b6fb8b2 100644 --- a/sources/scala/tools/nsc/backend/icode/GenICode.scala +++ b/sources/scala/tools/nsc/backend/icode/GenICode.scala @@ -23,12 +23,12 @@ abstract class GenICode extends SubComponent { import icodes._; import icodes.opcodes._; - val phaseName = "genicode"; + val phaseName = "icode"; override def newPhase(prev: Phase) = new ICodePhase(prev); class ICodePhase(prev: Phase) extends StdPhase(prev) { - override def name = "genicode"; + override def name = "icode"; override def description = "Generate ICode from the AST"; @@ -250,19 +250,26 @@ abstract class GenICode extends SubComponent { def genArrayOp(tree: Tree, ctx: Context, code: Int): Context = { import scalaPrimitives._; val Apply(Select(arrayObj, _), args) = tree; - var ctx1 = genLoad(arrayObj, ctx, toTypeKind(arrayObj.tpe)); + val k = toTypeKind(arrayObj.tpe); + val ARRAY(elem) = k; + var ctx1 = genLoad(arrayObj, ctx, k); if (scalaPrimitives.isArrayGet(code)) { - // load argument on stack - assert(args.length == 1, - "Too many arguments for array get operation: " + tree); - ctx1 = genLoad(args.head, ctx1, INT); + // load argument on stack + assert(args.length == 1, + "Too many arguments for array get operation: " + tree); + ctx1 = genLoad(args.head, ctx1, INT); + generatedType = elem; } else if (scalaPrimitives.isArraySet(code)) { assert(args.length == 2, "Too many arguments for array set operation: " + tree); ctx1 = genLoad(args.head, ctx1, INT); ctx1 = genLoad(args.tail.head, ctx1, toTypeKind(args.tail.head.tpe)); - } + // the following line should really be here, but because of bugs in erasure + // we pretend we generate whatever type is expected from us. + //generatedType = UNIT; + } else + generatedType = INT; code match { case ZARRAY_LENGTH => @@ -407,24 +414,23 @@ abstract class GenICode extends SubComponent { val outerCtx = ctx.dup; var bodyCtx: Context = null; + var afterCtx: Context = outerCtx.newBlock; + var finalHandler: Finalizer = null; + if (finalizer != EmptyTree) + finalHandler = ctx.newFinalizer; + else + finalHandler = NoFinalizer; + + + def genHandler = genExceptionHandler(ctx, outerCtx, afterCtx, finalHandler); val handlers = for (val CaseDef(pat, _, body) <- catches) yield pat match { case Typed(Ident(nme.WILDCARD), tpt) => - val exh = ctx.newHandler(tpt.tpe.symbol); - - val ctx1 = genLoad(body, outerCtx.enterHandler(exh), UNIT); - ctx1.bb.emit(RETURN(UNIT)); - ctx1.bb.close; - exh + genHandler(body, tpt.tpe.symbol); case Ident(nme.WILDCARD) => - val exh = ctx.newHandler(definitions.ThrowableClass); - - val ctx1 = genLoad(body, outerCtx.enterHandler(exh), UNIT); - ctx1.bb.emit(RETURN(UNIT)); - ctx1.bb.close; - exh + genHandler(body, definitions.ThrowableClass); case Bind(name, _) => val exception = new Local(pat.symbol, toTypeKind(pat.symbol.tpe)); @@ -434,8 +440,10 @@ abstract class GenICode extends SubComponent { val exhCtx = outerCtx.enterHandler(exh); exhCtx.bb.emit(STORE_LOCAL(exception, false), pat.pos); - val ctx1 = genLoad(body, exhCtx, UNIT); - ctx1.bb.emit(RETURN(UNIT)); + val ctx1 = genLoad(body, exhCtx, toTypeKind(body.tpe)); + if (finalHandler != NoFinalizer) + ctx1.bb.emit(CALL_FINALIZER(finalHandler)); + ctx1.bb.emit(JUMP(afterCtx.bb)); ctx1.bb.close; exh @@ -443,11 +451,13 @@ abstract class GenICode extends SubComponent { abort("Unknown exception case: " + pat + " at: " + unit.position(pat.pos)); } - val finalHandler = ctx.newFinalizer; - - val ctx1 = genLoad(finalizer, outerCtx.enterHandler(finalHandler), UNIT); - ctx1.bb.emit(RETURN(UNIT)); - ctx1.bb.close; + if (finalizer != EmptyTree) { + val finalizerCtx = outerCtx.enterHandler(finalHandler); + finalizerCtx.bb.emit(ENTER_FINALIZER(finalHandler)); + val ctx1 = genLoad(finalizer, finalizerCtx, UNIT); + ctx1.bb.emit(LEAVE_FINALIZER(finalHandler)); + ctx1.bb.close; + } bodyCtx = ctx.newBlock; outerCtx.bb.emit(JUMP(bodyCtx.bb), tree.pos); @@ -455,9 +465,18 @@ abstract class GenICode extends SubComponent { generatedType = toTypeKind(block.tpe); val ctxfinal = genLoad(block, bodyCtx, generatedType); - handlers.reverse foreach ctxfinal.exitHandler; + + handlers.reverse foreach (ex => { ex.finalizer = finalHandler; ctxfinal exitHandler ex }); ctxfinal.exitFinalizer(finalHandler); - ctxfinal +// if (generatedType != SCALA_ALL) + adapt(generatedType, expectedType, ctxfinal, tree); + + if (finalHandler != NoFinalizer) + ctxfinal.bb.emit(CALL_FINALIZER(finalHandler)); + ctxfinal.bb.emit(JUMP(afterCtx.bb)); + ctxfinal.bb.close; + generatedType = expectedType; + afterCtx case Throw(expr) => val ctx1 = genLoad(expr, ctx, THROWABLE); @@ -610,11 +629,29 @@ abstract class GenICode extends SubComponent { generatedType = BOOL; ctx1 = afterCtx; } else if (code == scalaPrimitives.SYNCHRONIZED) { + val monitor = new Local(ctx.method.symbol.newVariable(tree.pos, unit.fresh.newName("monitor")).setInfo(definitions.ObjectClass.tpe), + ANY_REF_CLASS); + ctx.method.addLocal(monitor); + ctx1 = genLoadQualifier(fun, ctx1); + ctx1.bb.emit(STORE_LOCAL(monitor, false)); ctx1.bb.emit(MONITOR_ENTER(), tree.pos); - ctx1 = genLoad(args.head, ctx1, toTypeKind(tree.tpe.resultType)); - ctx1 = genLoadQualifier(fun, ctx1); - ctx1.bb.emit(MONITOR_EXIT(), tree.pos); + + log("synchronized block start"); + ctx1 = ctx1.Try(NoSymbol, + bodyCtx => { + val ctx1 = genLoad(args.head, bodyCtx, toTypeKind(tree.tpe.resultType)); + ctx1.bb.emit(LOAD_LOCAL(monitor, false)); + ctx1.bb.emit(MONITOR_EXIT(), tree.pos); + ctx1 + }, + exhCtx => { + exhCtx.bb.emit(LOAD_LOCAL(monitor, false)); + exhCtx.bb.emit(MONITOR_EXIT(), tree.pos); + exhCtx.bb.emit(THROW()); + exhCtx + }); + log("synchronized block end with block " + ctx1.bb + " closed=" + ctx1.bb.isClosed); } else if (scalaPrimitives.isCoercion(code)) { ctx1 = genLoad(receiver, ctx1, toTypeKind(receiver.tpe)); genCoercion(tree, ctx1, code); @@ -780,30 +817,46 @@ abstract class GenICode extends SubComponent { } // emit conversion - if (!(generatedType <:< expectedType)) { - expectedType match { + if (generatedType != expectedType) + adapt(generatedType, expectedType, resCtx, tree); + + resCtx; + } + + private def adapt(from: TypeKind, to: TypeKind, ctx: Context, tree: Tree): Unit = { + if (!(from <:< to)) { + to match { case UNIT => - resCtx.bb.emit(DROP(generatedType), tree.pos); - log("Dropped an " + generatedType); + ctx.bb.emit(DROP(from), tree.pos); + log("Dropped an " + from); case _ => - assert(generatedType != UNIT, "Can't convert from UNIT to " + expectedType + + assert(from != UNIT, "Can't convert from UNIT to " + to + tree + " at: " + unit.position(tree.pos)); - resCtx.bb.emit(CALL_PRIMITIVE(Conversion(generatedType, expectedType)), tree.pos); + ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), tree.pos); } -// } else if (generatedType == SCALA_ALL && expectedType == UNIT) -// resCtx.bb.emit(DROP(generatedType)); - } else if (generatedType == SCALA_ALL) { - resCtx.bb.emit(DROP(generatedType)); - resCtx.bb.emit(getZeroOf(ctx.method.returnType)); - resCtx.bb.emit(RETURN(ctx.method.returnType)); - resCtx.bb.enterIgnoreMode; - } else if (generatedType == SCALA_ALLREF) { - resCtx.bb.emit(DROP(generatedType)); - resCtx.bb.emit(CONSTANT(Constant(null))); + } else if (from == SCALA_ALL) { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(getZeroOf(ctx.method.returnType)); + ctx.bb.emit(RETURN(ctx.method.returnType)); + ctx.bb.enterIgnoreMode; + } else if (from == SCALA_ALLREF) { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(CONSTANT(Constant(null))); } + } - resCtx; + private def genExceptionHandler(ctx: Context, outerCtx: Context, afterCtx: Context, finalHandler: Finalizer)(body: Tree, sym: Symbol): ExceptionHandler = { + val exh = ctx.newHandler(sym); + + var ctx1 = outerCtx.enterHandler(exh); + ctx1.bb.emit(DROP(REFERENCE(sym))); + ctx1 = genLoad(body, ctx1, toTypeKind(body.tpe)); + if (finalHandler != NoFinalizer) + ctx1.bb.emit(CALL_FINALIZER(finalHandler)); + ctx1.bb.emit(JUMP(afterCtx.bb)); + ctx1.bb.close; + exh } /** Load the qualifier of `tree' on top of the stack. */ @@ -1356,7 +1409,7 @@ abstract class GenICode extends SubComponent { handlers = handlers.tail; } - def exitFinalizer(f: Finalizer): Unit = { + def exitFinalizer(f: Finalizer): Unit = if (f != NoFinalizer) { assert(handlerCount > 0 && finalizers.head == f, "Wrong nesting of exception handlers." + this + " for " + f); handlerCount = handlerCount - 1; @@ -1365,6 +1418,28 @@ abstract class GenICode extends SubComponent { /** Clone the current context */ def dup: Context = new Context(this); + + def Try(catched: Symbol, body: Context => Context, handler: Context => Context) = { + val outerCtx = this.dup; + val afterCtx = outerCtx.newBlock; + val exh = this.newHandler(catched); + + val ctx1 = handler(outerCtx.enterHandler(exh)); + ctx1.bb.emit(JUMP(afterCtx.bb)); + ctx1.bb.close; + + val bodyCtx = this.newBlock; + val finalCtx = body(bodyCtx); + + outerCtx.bb.emit(JUMP(bodyCtx.bb)); + outerCtx.bb.close; + + finalCtx.exitHandler(exh); + finalCtx.bb.emit(JUMP(afterCtx.bb)); + finalCtx.bb.close; + + afterCtx + } } /** diff --git a/sources/scala/tools/nsc/backend/icode/Linearizers.scala b/sources/scala/tools/nsc/backend/icode/Linearizers.scala index 7f6e71e8ed..baffccef77 100644 --- a/sources/scala/tools/nsc/backend/icode/Linearizers.scala +++ b/sources/scala/tools/nsc/backend/icode/Linearizers.scala @@ -8,13 +8,13 @@ package scala.tools.nsc.backend.icode; import scala.tools.nsc.ast._; -import scala.collection.mutable.Queue; +import scala.collection.mutable.Stack; trait Linearizers: ICodes { import opcodes._; trait Linearizer { - def linearize(c: Code): List[BasicBlock]; + def linearize(c: IMethod): List[BasicBlock]; } /** @@ -26,43 +26,54 @@ trait Linearizers: ICodes { */ class NormalLinearizer extends Linearizer with WorklistAlgorithm { type Elem = BasicBlock; - val worklist: Queue[Elem] = new Queue(); + type WList = Stack[Elem]; + + val worklist: WList = new Stack(); var blocks: List[BasicBlock] = Nil; - def linearize(c: Code): List[BasicBlock] = { - val b = c.startBlock; - blocks = b :: Nil; + def linearize(m: IMethod): List[BasicBlock] = { + val b = m.code.startBlock; + blocks = Nil; + + run { + worklist ++= (m.exh map (.startBlock)); + worklist ++= (m.finalizers map (.startBlock)); + worklist.push(b); + } - run( { worklist.enqueue(b); } ); blocks.reverse; } + /** Linearize another subtree and append it to the existing blocks. */ def linearize(startBlock: BasicBlock): List[BasicBlock] = { - blocks = startBlock :: Nil; - run( { worklist.enqueue(startBlock); } ); + //blocks = startBlock :: Nil; + run( { worklist.push(startBlock); } ); blocks.reverse; } def processElement(b: BasicBlock) = - if (b.size > 0) - b.lastInstruction match { - case JUMP(where) => - add(where); - case CJUMP(success, failure, _, _) => - add(success); - add(failure); - case CZJUMP(success, failure, _, _) => - add(success); - add(failure); - case SWITCH(_, labels) => - add(labels); - case RETURN(_) => - () - case THROW() => - () + if (b.size > 0) { + add(b); + b.lastInstruction match { + case JUMP(where) => + add(where); + case CJUMP(success, failure, _, _) => + add(success); + add(failure); + case CZJUMP(success, failure, _, _) => + add(success); + add(failure); + case SWITCH(_, labels) => + add(labels); + case RETURN(_) => (); + case THROW() => (); + case LEAVE_FINALIZER(_) => (); + } } + def dequeue: Elem = worklist.pop; + /** * Prepend b to the list, if not already scheduled. * TODO: use better test than linear search @@ -72,7 +83,7 @@ trait Linearizers: ICodes { () else { blocks = b :: blocks; - worklist enqueue b; + worklist push b; } def add(bs: List[BasicBlock]): Unit = bs foreach add; diff --git a/sources/scala/tools/nsc/backend/icode/Members.scala b/sources/scala/tools/nsc/backend/icode/Members.scala index 3f86291909..50dd3afb5f 100644 --- a/sources/scala/tools/nsc/backend/icode/Members.scala +++ b/sources/scala/tools/nsc/backend/icode/Members.scala @@ -8,7 +8,7 @@ package scala.tools.nsc.backend.icode; import scala.collection.mutable.HashMap; -import scala.collection.mutable.HashSet; +import scala.collection.mutable.{Set, HashSet}; import scala.{Symbol => scala_Symbol}; import scala.tools.nsc.symtab.Flags; @@ -37,9 +37,29 @@ trait Members: ICodes { startBlock = newBlock; startBlock.initStack(new TypeStack); - /** Apply a function to all basic blocks, for side-effects. */ - def traverse(f: BasicBlock => Unit) = - traverseFeedBack((bb: BasicBlock, hm: HashMap[BasicBlock, boolean]) => f(bb)); + /** + * Apply a function to all basic blocks, for side-effects. It starts at + * the given startBlock and checks that are no predecessors of the given node. + * Only blocks that are reachable via a path from startBlock are ever visited. + */ + def traverseFrom(startBlock: BasicBlock, f: BasicBlock => Unit) = { + val visited: Set[BasicBlock] = new HashSet(); + + def traverse0(toVisit: List[BasicBlock]): Unit = toVisit match { + case Nil => (); + case b :: bs => if (!visited.contains(b)) { + f(b); + visited += b; + traverse0(bs ::: b.successors); + } else + traverse0(bs); + } + assert(startBlock.predecessors == Nil, + "Starting traverse from a block with predecessors: " + this); + traverse0(startBlock :: Nil) + } + + def traverse(f: BasicBlock => Unit) = blocks foreach f; /* This method applies the given function to each basic block. */ def traverseFeedBack(f: (BasicBlock, HashMap[BasicBlock, Boolean]) => Unit) = { @@ -65,25 +85,25 @@ trait Members: ICodes { override def toString() : String = "ICode '" + label + "'"; /** This method print the code */ - def print() : unit = print(System.out); - - def print(out: java.io.PrintStream) : unit = { - traverse((bb: BasicBlock) => { - out.println("Block #" + bb.label); - out.println("Substituable variables : "); - if (bb.substituteVars != null) - bb.substituteVars.foreach(out.print); - else - out.println(" {Empty} "); - out.println("Instructions:"); - bb.traverse((ici: Instruction) => - out.println(" "+ici.toString())); - out.print ("Successors: "); - bb.successors.foreach((bb: BasicBlock) => out.print(bb.label+", ")); - out.println (""); // ?? Del - out.println (); - }); - } +// def print() : unit = print(System.out); + +// def print(out: java.io.PrintStream) : unit = { +// traverse((bb: BasicBlock) => { +// out.println("Block #" + bb.label); +// out.println("Substituable variables : "); +// if (bb.substituteVars != null) +// bb.substituteVars.foreach(out.print); +// else +// out.println(" {Empty} "); +// out.println("Instructions:"); +// bb.traverse((ici: Instruction) => +// out.println(" "+ici.toString())); +// out.print ("Successors: "); +// bb.successors.foreach((bb: BasicBlock) => out.print(bb.label+", ")); +// out.println (""); // ?? Del +// out.println (); +// }); +// } /* Compute a unique new label */ def nextLabel = { diff --git a/sources/scala/tools/nsc/backend/icode/Opcodes.scala b/sources/scala/tools/nsc/backend/icode/Opcodes.scala index ec5fb429d4..3f2fb45b17 100644 --- a/sources/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/sources/scala/tools/nsc/backend/icode/Opcodes.scala @@ -26,6 +26,8 @@ import scala.tools.util.Position; case CALL_PRIMITIVE(primitive) => case CALL_METHOD(method, style) => case CALL_FINALIZER(finalizer) => + case ENTER_FINALIZER(finalizer) => + case LEAVE_FINALIZER(finalizer) => case NEW(kind) => case CREATE_ARRAY(elem) => case IS_INSTANCE(tpe) => @@ -236,13 +238,28 @@ import scala.tools.util.Position; } case class CALL_FINALIZER(finalizer: Finalizer) extends Instruction { - + assert(finalizer != NoFinalizer, "CALL_FINALIZER cannot call NoFinalizer"); override def toString(): String = "CALL_FINALIZER " + finalizer; override def consumed = 0; + override def produced = 0; + } - override def produced = 1; + case class ENTER_FINALIZER(finalizer: Finalizer) extends Instruction { + override def toString(): String = + "ENTER_FINALIZER " + finalizer; + + override def consumed = 1; + override def produced = 0; + } + + case class LEAVE_FINALIZER(finalizer: Finalizer) extends Instruction { + override def toString(): String = + "LEAVE_FINALIZER " + finalizer; + + override def consumed = 0; + override def produced = 0; } /** Create a new instance of a class through the specified constructor diff --git a/sources/scala/tools/nsc/backend/icode/Printers.scala b/sources/scala/tools/nsc/backend/icode/Printers.scala index de773b1d94..2ec3e00fd8 100644 --- a/sources/scala/tools/nsc/backend/icode/Printers.scala +++ b/sources/scala/tools/nsc/backend/icode/Printers.scala @@ -85,7 +85,7 @@ abstract class Printers { if (!m.isDeferred) { println(" {"); - printCode(m.code); + linearizer.linearize(m) foreach printBlock; println("}"); indent;println("Exception handlers: "); @@ -102,14 +102,12 @@ abstract class Printers { print(" ("); print(p.kind); print(")"); } - def printCode(code: Code): Unit = { -// code traverse printBlock; - linearizer.linearize(code) foreach printBlock; - } - def printExceptionHandler(e: ExceptionHandler) = { - println(" catch (" + e.cls.simpleName + ") in " + e.covered); - linearizer.linearize(e.startBlock) foreach printBlock; + indent; + println("catch (" + e.cls.simpleName + ") in " + e.covered + " starting at: " + e.startBlock); + undent; + println("with finalizer: " + e.finalizer); +// linearizer.linearize(e.startBlock) foreach printBlock; } def printBlock(bb: BasicBlock): Unit = { diff --git a/sources/scala/tools/nsc/backend/jvm/GenJVM.scala b/sources/scala/tools/nsc/backend/jvm/GenJVM.scala index 7f169c7bdb..df117c7146 100644 --- a/sources/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/sources/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -132,6 +132,7 @@ abstract class GenJVM extends SubComponent { log("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) + " owner: " + m.symbol.owner); method = m; + endPC.clear; computeLocalVarsIndex(m); var resTpe = javaType(toTypeKind(m.symbol.tpe.resultType)); @@ -157,7 +158,7 @@ abstract class GenJVM extends SubComponent { jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym)); jcode = jmethod.getCode().asInstanceOf[JExtendedCode]; - genCode(m.code); + genCode(m); } } @@ -224,12 +225,98 @@ abstract class GenJVM extends SubComponent { val linearizer = new NormalLinearizer(); + var linearization: List[BasicBlock] = Nil; - def genCode(c: Code): Unit = { - code = c; - val blocks = linearizer.linearize(code); - makeLabels(blocks); - blocks foreach genBlock; + def genCode(m: IMethod): Unit = { + labels.clear; + retAddress.clear; + anyHandler.clear; + + code = m.code; + linearization = linearizer.linearize(m); + makeLabels(linearization); + linearization foreach genBlock; + if (this.method.exh != Nil || this.method.finalizers != Nil) + genExceptionHandlers; + } + + /** Map from finalizer to the code area where its 'any' exception handler was generated. */ + val anyHandler: HashMap[Finalizer, Pair[Int, Int]] = new HashMap(); + + /** Generate exception handlers for the current method. */ + def genExceptionHandlers: Unit = { + + def ranges(e: ExceptionHandler): List[Pair[Int, Int]] = { + var covered = e.covered; + var ranges: List[Pair[Int, Int]] = Nil; + var start = -1; + var end = -1; + + linearization foreach ((b) => { + if (! (covered contains b) ) { + if (start >= 0) { // we're inside a handler range + end = labels(b).getAnchor(); + ranges = Pair(start, end) :: ranges; + log("ending range: " + ranges.head); + start = -1; + } + } else { + if (start >= 0) { // we're inside a handler range + end = endPC(b); + log("appending block " + b + " to current range: " + Pair(start, end)); + } else { + start = labels(b).getAnchor(); + end = endPC(b); + log("starting new range with " + b + ": " + Pair(start, end)); + } + covered = covered remove b.==; + } + }); + + if (start >= 0) { + ranges = Pair(start, end) :: ranges; + log("adding last range: " + ranges.head); + } + + if (covered != Nil) + log("Some covered blocks were not found in method: " + method + + " covered: " + covered + " not in " + linearization); + ranges + } + + this.method.exh foreach ((e) => { + ranges(e) foreach ((p) => { + log("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method + + " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls); + jcode.addExceptionHandler(p._1, p._2, + labels(e.startBlock).getAnchor(), + if (e.cls == NoSymbol) + null + else javaName(e.cls)) + }) + }); + this.method.finalizers foreach ((f) => { + val targetPC = jcode.getPC(); + val exceptionLocal = jmethod.addNewLocalVariable(JObjectType.JAVA_LANG_OBJECT, clasz.cunit.fresh.newName("exception")); + jcode.emitSTORE(exceptionLocal); + jcode.emitJSR(labels(f.startBlock)); + jcode.emitLOAD(exceptionLocal); + jcode.emitATHROW(); + + log("Finalizer: " + f + " coveres: " + f.covered); + anyHandler.foreach((of: Finalizer, r: Pair[Int, Int]) => + if (f.covered.contains(of.startBlock)) + jcode.addFinallyHandler(r._1, r._2, targetPC)); + + anyHandler += f -> Pair(targetPC, jcode.getPC()); + + ranges(f) foreach ((p) => { + log("Adding finalizer handler " + f + "at: " + targetPC + " for " + method + + " from: " + p._1 + " to: " + p._2); + jcode.addFinallyHandler(p._1, p._2, targetPC); + }); + + }); } def genBlock(b: BasicBlock): Unit = { @@ -241,6 +328,9 @@ abstract class GenJVM extends SubComponent { var crtPC = 0; b traverse ( instr => { + if (b.lastInstruction == instr) + endPC(b) = jcode.getPC(); + instr match { case THIS(clasz) => jcode.emitALOAD_0(); @@ -273,8 +363,8 @@ abstract class GenJVM extends SubComponent { case LOAD_FIELD(field, isStatic) => var owner = javaName(field.owner); // if (field.owner.hasFlag(Flags.MODULE)) owner = owner + "$"; - - log("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)); + if (settings.debug.value) + log("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)); if (isStatic) jcode.emitGETSTATIC(owner, javaName(field), @@ -286,8 +376,9 @@ abstract class GenJVM extends SubComponent { case LOAD_MODULE(module) => assert(module.isModule || module.isModuleClass, "Expected module: " + module); - log("genearting LOAD_MODULE for: " + module + " flags: " + - Flags.flagsToString(module.flags)); + if (settings.debug.value) + log("genearting LOAD_MODULE for: " + module + " flags: " + + Flags.flagsToString(module.flags)); jcode.emitGETSTATIC(javaName(module) /* + "$" */ , MODULE_INSTANCE_NAME, javaType(module)); @@ -357,6 +448,16 @@ abstract class GenJVM extends SubComponent { javaType(method).asInstanceOf[JMethodType]); } + case CALL_FINALIZER(finalizer) => + jcode.emitJSR(labels(finalizer.startBlock)); + + case ENTER_FINALIZER(finalizer) => + retAddress(finalizer) = jmethod.addNewLocalVariable(JType.ADDRESS, clasz.cunit.fresh.newName("ret")); + jcode.emitSTORE(retAddress(finalizer)); + + case LEAVE_FINALIZER(finalizer) => + jcode.emitRET(retAddress(finalizer)); + case NEW(REFERENCE(cls)) => val className = javaName(cls); jcode.emitNEW(className); @@ -393,7 +494,8 @@ abstract class GenJVM extends SubComponent { caze = caze.tail; } val branchArray = new Array[JCode$Label](tagArray.length); - log("Emitting SWITHCH:\ntags: " + tags + "\nbranches: " + branches); + if (settings.debug.value) + log("Emitting SWITHCH:\ntags: " + tags + "\nbranches: " + branches); jcode.emitSWITCH(tagArray, (branches map labels dropRight 1).copyToArray(branchArray, 0), labels(branches.last), @@ -603,7 +705,8 @@ abstract class GenJVM extends SubComponent { } case Conversion(src, dst) => - log("Converting from: " + src + " to: " + dst); + if (settings.debug.value) + log("Converting from: " + src + " to: " + dst); if (dst == BOOL) { Console.println("Illegal conversion at: " + clasz + " at: " + method.sourceFile + ":" + Position.line(pos)); @@ -638,8 +741,10 @@ abstract class GenJVM extends SubComponent { } } + val endPC: HashMap[BasicBlock, Int] = new HashMap(); val labels: HashMap[BasicBlock, JCode$Label] = new HashMap(); val conds: HashMap[TestOp, Int] = new HashMap(); + val retAddress: HashMap[Finalizer, JLocalVariable] = new HashMap(); conds += EQ -> JExtendedCode.COND_EQ; conds += NE -> JExtendedCode.COND_NE; @@ -649,7 +754,7 @@ abstract class GenJVM extends SubComponent { conds += GE -> JExtendedCode.COND_GE; def makeLabels(bs: List[BasicBlock]) = { - labels.clear; + //labels.clear; log("Making labels for: " + method); bs foreach (bb => labels += bb -> jcode.newLabel() ); } |