diff options
author | Paul Phillips <paulp@improving.org> | 2010-11-08 18:13:08 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-11-08 18:13:08 +0000 |
commit | 942bf86c7bb28c7d9aeae82204ae060ae330d9b6 (patch) | |
tree | b88f130c5294617439949d77858b3e4538eb223f /src | |
parent | 8009f723b905509e9dfa22d8746787ebb0cd11a2 (diff) | |
download | scala-942bf86c7bb28c7d9aeae82204ae060ae330d9b6.tar.gz scala-942bf86c7bb28c7d9aeae82204ae060ae330d9b6.tar.bz2 scala-942bf86c7bb28c7d9aeae82204ae060ae330d9b6.zip |
This is work on -Ycheck:icode.
distribution can now be built with that option, with or without
optimization, and almost all tests cases can. (Those which can't are due
to different -Ycheck: issues.)
Major changes of interest are as follows:
* LOAD_EXCEPTION and THROW are parameterized on the throwable symbol.
* Does not squash all traits down to AnyRef, but instead deals with
issues as they arise. By observation the cases where one needs a "Foo
with Product" to manifest as both a "Foo" and a "Product" at different
places are quite rare, so we need not throw out the whole baby. *
Exception handlers now have positions. * The remaining checker failures
removed, such as CALL_METHOD wanting to pop a value off the stack
after calling a constructor. * Many multiply defined values such as
REFERENCE(ObjectClass) put in one place (ICodes.scala) and reused. *
-Ycheck:icode output (if also given -Ydebug) worthy of Michelangelo.
Here is a class and the -Ycheck:icode -Ydebug output for f's block.
class A {
def f(x: Int, y: String) =
try println(x + y.length)
catch { case x: NullPointerException => () }
}
** Checking Block 4 [S: 3, 2] [P: 1] <closed>
1-> REF(singleton class Predef) 3 + LOAD_MODULE object Predef
2-> INT 3 + LOAD_LOCAL(value x)
3-> REF(class String) 3 + LOAD_LOCAL(value y)
2<- REF(class String) 3 - CALL_METHOD java.lang.String.length (dynamic)
3-> INT 3 + CALL_METHOD java.lang.String.length (dynamic)
2<- INT 3 - CALL_PRIMITIVE(Arithmetic(ADD,INT))
1<- INT 3 - """
2-> INT 3 + CALL_PRIMITIVE(Arithmetic(ADD,INT))
1<- INT 3 - BOX INT
2-> REF(class Integer) 3 + BOX INT
1<- REF(class Integer) 3 - CALL_METHOD scala.Predef.println (dynamic)
0<- REF(singleton class Predef) 3 - CALL_METHOD scala.Predef.println (dynamic)
Review by dragos (I marked the specific spots I thought of interest with
"PP to ID" which makes it sound like I'm talking to my primal self. Next
week on programmer theater: "PP to SUPEREGO.")
Diffstat (limited to 'src')
19 files changed, 486 insertions, 400 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index c09851fa86..45d1916ca9 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -256,7 +256,7 @@ trait BasicBlocks { * put into ignore mode so we hear about it if there's a problem. */ instr match { - case JUMP(_) | RETURN(_) | THROW() | SCOPE_EXIT(_) => // ok + case JUMP(_) | RETURN(_) | THROW(_) | SCOPE_EXIT(_) => // ok case STORE_LOCAL(local) if isExceptionResultName(local.sym.name) => // ok case x => log("Ignoring instruction, possibly at our peril, at " + pos + ": " + x) } @@ -373,7 +373,7 @@ trait BasicBlocks { case CZJUMP(succ, fail, _, _) => fail :: succ :: Nil case SWITCH(_, labels) => labels case RETURN(_) => Nil - case THROW() => Nil + case THROW(_) => Nil case _ => if (closed) { dump @@ -422,40 +422,50 @@ trait BasicBlocks { private def succString = if (successors.isEmpty) "[S: N/A]" else successors.distinct.mkString("[S: ", ", ", "]") private def predString = if (predecessors.isEmpty) "[P: N/A]" else predecessors.distinct.mkString("[P: ", ", ", "]") - def fullString: String = List("Block", label, succString, predString) mkString " " override def toString(): String = "" + label - def flagsString: String = - ("block " + label + ( - if (hasFlag(LOOP_HEADER)) " <loopheader> " - else if (hasFlag(IGNORING)) " <ignore> " - else if (hasFlag(EX_HEADER)) " <exheader> " - else if (hasFlag(CLOSED)) " <closed> " - else if (hasFlag(DIRTYSUCCS)) " <dirtysuccs> " - else if (hasFlag(DIRTYPREDS)) " <dirtypreds> " - else "" - )) - } + def blockContents = { + def posStr(p: Position) = if (p.isDefined) p.line.toString else "<??>" + val xs = this.toList map (instr => posStr(instr.pos) + "\t" + instr) + xs.mkString(fullString + " {\n ", "\n ", "\n}") + } + def predContents = predecessors.map(_.blockContents).mkString(predecessors.size + " preds:\n", "\n", "\n") + def succContents = successors.map(_.blockContents).mkString(successors.size + " succs:\n", "\n", "\n") + def fullString: String = List("Block", label, succString, predString, flagsString) mkString " " + def flagsString: String = BBFlags.flagsToString(flags) + } } object BBFlags { + val flagMap = Map[Int, String]( + LOOP_HEADER -> "loopheader", + IGNORING -> "ignore", + EX_HEADER -> "exheader", + CLOSED -> "closed", + DIRTYSUCCS -> "dirtysuccs", + DIRTYPREDS -> "dirtypreds" + ) + def flagsToString(flags: Int) = { + flagMap collect { case (bit, name) if (bit & flags) != 0 => "<" + name + ">" } mkString " " + } + /** This block is a loop header (was translated from a while). */ - final val LOOP_HEADER = 0x00000001 + final val LOOP_HEADER = (1 << 0) /** Ignoring mode: emit instructions are dropped. */ - final val IGNORING = 0x00000002 + final val IGNORING = (1 << 1) /** This block is the header of an exception handler. */ - final val EX_HEADER = 0x00000004 + final val EX_HEADER = (1 << 2) /** This block is closed. No new instructions can be added. */ - final val CLOSED = 0x00000008 + final val CLOSED = (1 << 3) /** Code has been changed, recompute successors. */ - final val DIRTYSUCCS = 0x00000010 + final val DIRTYSUCCS = (1 << 4) /** Code has been changed, recompute predecessors. */ - final val DIRTYPREDS = 0x00000020 + final val DIRTYPREDS = (1 << 5) } diff --git a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala index e5b94076e8..89c5ca18a9 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala @@ -6,9 +6,10 @@ package scala.tools.nsc package backend -package icode; +package icode -import scala.collection._ +import scala.collection.{ mutable, immutable, generic } +import util.{ Position, NoPosition } /** * Exception handlers are pieces of code that `handle' exceptions on @@ -17,10 +18,14 @@ import scala.collection._ * all our handlers will catch `Throwable' and rely on proper ordering * in the generated code to preserve nesting. */ -trait ExceptionHandlers { self: ICodes => - import global.{Symbol, NoSymbol}; +trait ExceptionHandlers { + self: ICodes => - class ExceptionHandler(val method: IMethod, val label: String, val cls: Symbol) { + import global.{ definitions, Symbol, NoSymbol } + import definitions.{ ThrowableClass } + + class ExceptionHandler(val method: IMethod, val label: String, val cls: Symbol, val pos: Position) { + def loadExceptionClass = if (cls == NoSymbol) ThrowableClass else cls private var _startBlock: BasicBlock = _; var finalizer: Finalizer = _; @@ -31,18 +36,18 @@ trait ExceptionHandlers { self: ICodes => _startBlock = b; b.exceptionHandlerStart = true } - def startBlock = _startBlock; + def startBlock = _startBlock /** The list of blocks that are covered by this exception handler */ var covered: immutable.Set[BasicBlock] = immutable.HashSet.empty[BasicBlock] - def addCoveredBlock(b: BasicBlock): ExceptionHandler = { + def addCoveredBlock(b: BasicBlock): this.type = { covered = covered + b this } /** Is `b' covered by this exception handler? */ - def covers(b: BasicBlock): Boolean = covered(b); + def covers(b: BasicBlock): Boolean = covered(b) /** The body of this exception handler. May contain 'dead' blocks (which will not * make it into generated code because linearizers may not include them) */ @@ -54,23 +59,24 @@ trait ExceptionHandlers { self: ICodes => /** A standard copy constructor */ def this(other: ExceptionHandler) = { - this(other.method, other.label, other.cls); - covered = other.covered; - setStartBlock(other.startBlock); - finalizer = other.finalizer; + this(other.method, other.label, other.cls, other.pos) + + covered = other.covered + setStartBlock(other.startBlock) + finalizer = other.finalizer } - def dup: ExceptionHandler = new ExceptionHandler(this); + def dup: ExceptionHandler = new ExceptionHandler(this) } - class Finalizer(method: IMethod, label: String) extends ExceptionHandler(method, label, NoSymbol) { - override def toString() = "finalizer_" + label; - - override def dup: Finalizer = new Finalizer(method, label); + class Finalizer(method: IMethod, label: String, pos: Position) extends ExceptionHandler(method, label, NoSymbol, pos) { + override def toString() = "finalizer_" + label + override def dup: Finalizer = new Finalizer(method, label, pos) } - object NoFinalizer extends Finalizer(null, "<no finalizer>") { - override def startBlock: BasicBlock = error("NoFinalizer cannot have a start block."); + object NoFinalizer extends Finalizer(null, "<no finalizer>", NoPosition) { + override def startBlock: BasicBlock = error("NoFinalizer cannot have a start block."); override def setStartBlock(b: BasicBlock): Unit = error("NoFinalizer cannot have a start block."); + override def dup = this } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index a835ac5eb9..d68b678610 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -50,15 +50,6 @@ abstract class GenICode extends SubComponent { var unit: CompilationUnit = _ - // We assume definitions are already initialized - val STRING = REFERENCE(StringClass) - - // this depends on the backend! should be changed. - val ANY_REF_CLASS = REFERENCE(ObjectClass) - val SCALA_NOTHING = REFERENCE(NothingClass) - val SCALA_NULL = REFERENCE(NullClass) - val THROWABLE = REFERENCE(ThrowableClass) - override def run { scalaPrimitives.init classes.clear @@ -184,6 +175,18 @@ abstract class GenICode extends SubComponent { case _ => genLoad(tree, ctx, UNIT) } + + private def genThrow(expr: Tree, ctx: Context): (Context, TypeKind) = { + require(expr.tpe <:< ThrowableClass.tpe) + + val thrownKind = toTypeKind(expr.tpe) + val ctx1 = genLoad(expr, ctx, thrownKind) + ctx1.bb.emit(THROW(expr.tpe.typeSymbol), expr.pos) + ctx1.bb.enterIgnoreMode + + (ctx1, NothingReference) + } + /** * Generate code for primitive arithmetic operations. * Returns (Context, Generated Type) @@ -310,7 +313,7 @@ abstract class GenICode extends SubComponent { var ctx1 = genLoadQualifier(fun, ctx) ctx1.bb.emit(Seq( - DUP(ANY_REF_CLASS), + DUP(ObjectReference), STORE_LOCAL(monitor), MONITOR_ENTER() setPos tree.pos )) @@ -333,7 +336,7 @@ abstract class GenICode extends SubComponent { exhCtx.bb.emit(Seq( LOAD_LOCAL(monitor), MONITOR_EXIT() setPos tree.pos, - THROW() + THROW(ThrowableClass) )) exhCtx.bb.enterIgnoreMode exhCtx @@ -428,7 +431,7 @@ abstract class GenICode extends SubComponent { if (scalaPrimitives.isArithmeticOp(code)) genArithmeticOp(tree, ctx, code) else if (code == scalaPrimitives.CONCAT) - (genStringConcat(tree, ctx), STRING) + (genStringConcat(tree, ctx), StringReference) else if (code == scalaPrimitives.HASH) (genScalaHash(receiver, ctx), INT) else if (isArrayOp(code)) @@ -709,10 +712,8 @@ abstract class GenICode extends SubComponent { case t @ Try(_, _, _) => genLoadTry(t, ctx, (x: TypeKind) => generatedType = x) case Throw(expr) => - val ctx1 = genLoad(expr, ctx, THROWABLE) - ctx1.bb.emit(THROW(), tree.pos) - ctx1.bb.enterIgnoreMode - generatedType = SCALA_NOTHING + val (ctx1, expectedType) = genThrow(expr, ctx) + generatedType = expectedType ctx1 case New(tpt) => @@ -737,9 +738,9 @@ abstract class GenICode extends SubComponent { ctx1.bb.emit(DROP(l), fun.pos) if (cast) { ctx1.bb.emit(Seq( - NEW(REFERENCE(definitions.getClass("ClassCastException"))), - DUP(ANY_REF_CLASS), - THROW() + NEW(REFERENCE(definitions.ClassCastExceptionClass)), + DUP(ObjectReference), + THROW(definitions.ClassCastExceptionClass) )) } else ctx1.bb.emit(CONSTANT(Constant(false))) @@ -875,22 +876,19 @@ abstract class GenICode extends SubComponent { val sym = fun.symbol if (sym.isLabel) { // jump to a label - val label = ctx.labels.get(sym) match { - case Some(l) => l - + val label = ctx.labels.getOrElse(sym, { // it is a forward jump, scan for labels - case None => - log("Performing scan for label because of forward jump.") - scanForLabels(ctx.defdef, ctx) - ctx.labels.get(sym) match { - case Some(l) => - log("Found label: " + l) - l - case _ => - abort("Unknown label target: " + sym + - " at: " + (fun.pos) + ": ctx: " + ctx) - } - } + log("Performing scan for label because of forward jump.") + scanForLabels(ctx.defdef, ctx) + ctx.labels.get(sym) match { + case Some(l) => + log("Found label: " + l) + l + case _ => + abort("Unknown label target: " + sym + + " at: " + (fun.pos) + ": ctx: " + ctx) + } + }) val ctx1 = genLoadLabelArguments(args, label, ctx) ctx1.bb.emitOnly(if (label.anchored) JUMP(label.block) else PJUMP(label)) ctx1.bb.enterIgnoreMode @@ -950,7 +948,7 @@ abstract class GenICode extends SubComponent { case ApplyDynamic(qual, args) => assert(!forMSIL) ctx.clazz.bootstrapClass = Some("scala.runtime.DynamicDispatch") - val ctx1 = genLoad(qual, ctx, ANY_REF_CLASS) + val ctx1 = genLoad(qual, ctx, ObjectReference) genLoadArguments(args, tree.symbol.info.paramTypes, ctx1) ctx1.bb.emit(CALL_METHOD(tree.symbol, InvokeDynamic), tree.pos) ctx1 @@ -1036,7 +1034,7 @@ abstract class GenICode extends SubComponent { generatedType = DOUBLE case (NullTag, _) => ctx.bb.emit(CONSTANT(value), tree.pos); - generatedType = SCALA_NULL + generatedType = NullReference case _ => ctx.bb.emit(CONSTANT(value), tree.pos); generatedType = toTypeKind(tree.tpe) @@ -1129,7 +1127,7 @@ abstract class GenICode extends SubComponent { } private def adapt(from: TypeKind, to: TypeKind, ctx: Context, pos: Position): Unit = { - if (!(from <:< to) && !(from == SCALA_NULL && to == SCALA_NOTHING)) { + if (!(from <:< to) && !(from == NullReference && to == NothingReference)) { to match { case UNIT => ctx.bb.emit(DROP(from), pos) @@ -1142,14 +1140,14 @@ abstract class GenICode extends SubComponent { assert(!from.isReferenceType && !to.isReferenceType, "type error: can't convert from " + from + " to " + to +" in unit "+this.unit) ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), pos); } - } else if (from == SCALA_NOTHING) { - ctx.bb.emit(THROW()) + } else if (from == NothingReference) { + ctx.bb.emit(THROW(ThrowableClass)) ctx.bb.enterIgnoreMode - } else if (from == SCALA_NULL) { + } else if (from == NullReference) { ctx.bb.emit(DROP(from)) ctx.bb.emit(CONSTANT(Constant(null))) } - else if (from == THROWABLE) { + else if (from == ThrowableReference) { log("Inserted check-cast on throwable to " + to + " at " + pos) ctx.bb.emit(CHECK_CAST(to)) } @@ -1351,7 +1349,7 @@ abstract class GenICode extends SubComponent { getMember(ScalaRunTimeModule, "hash") } - val ctx1 = genLoad(tree, ctx, ANY_REF_CLASS) + val ctx1 = genLoad(tree, ctx, ObjectReference) ctx1.bb.emit(CALL_METHOD(hashMethod, Static(false))) ctx1 } @@ -1429,9 +1427,9 @@ abstract class GenICode extends SubComponent { // special-case reference (in)equality test for null (null eq x, x eq null) lazy val nonNullSide = ifOneIsNull(l, r) if (isReferenceEqualityOp(code) && nonNullSide != null) { - val ctx1 = genLoad(nonNullSide, ctx, ANY_REF_CLASS) + val ctx1 = genLoad(nonNullSide, ctx, ObjectReference) ctx1.bb.emitOnly( - CZJUMP(thenCtx.bb, elseCtx.bb, op, ANY_REF_CLASS) + CZJUMP(thenCtx.bb, elseCtx.bb, op, ObjectReference) ) } else { @@ -1557,8 +1555,8 @@ abstract class GenICode extends SubComponent { getMember(ScalaRunTimeModule, nme.inlinedEquals) } - val ctx1 = genLoad(l, ctx, ANY_REF_CLASS) - val ctx2 = genLoad(r, ctx1, ANY_REF_CLASS) + val ctx1 = genLoad(l, ctx, ObjectReference) + val ctx2 = genLoad(r, ctx1, ObjectReference) ctx2.bb.emit(CALL_METHOD(equalsMethod, if (settings.XO.value) Dynamic else Static(false))) ctx2.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)) ctx2.bb.close @@ -1566,30 +1564,30 @@ abstract class GenICode extends SubComponent { else { if (isNull(l)) // null == expr -> expr eq null - genLoad(r, ctx, ANY_REF_CLASS).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS) + genLoad(r, ctx, ObjectReference).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference) else if (isNull(r)) { // expr == null -> expr eq null - genLoad(l, ctx, ANY_REF_CLASS).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS) + genLoad(l, ctx, ObjectReference).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference) } else { val eqEqTempLocal = getTempLocal - var ctx1 = genLoad(l, ctx, ANY_REF_CLASS) + var ctx1 = genLoad(l, ctx, ObjectReference) // dicey refactor section lazy val nonNullCtx = ctx1.newBlock // l == r -> if (l eq null) r eq null else l.equals(r) - ctx1 = genLoad(r, ctx1, ANY_REF_CLASS) + ctx1 = genLoad(r, ctx1, ObjectReference) val nullCtx = ctx1.newBlock ctx1.bb.emitOnly( STORE_LOCAL(eqEqTempLocal) setPos l.pos, - DUP(ANY_REF_CLASS), - CZJUMP(nullCtx.bb, nonNullCtx.bb, EQ, ANY_REF_CLASS) + DUP(ObjectReference), + CZJUMP(nullCtx.bb, nonNullCtx.bb, EQ, ObjectReference) ) nullCtx.bb.emitOnly( - DROP(ANY_REF_CLASS) setPos l.pos, // type of AnyRef + DROP(ObjectReference) setPos l.pos, // type of AnyRef LOAD_LOCAL(eqEqTempLocal), - CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS) + CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference) ) nonNullCtx.bb.emitOnly( LOAD_LOCAL(eqEqTempLocal) setPos l.pos, @@ -1873,13 +1871,9 @@ abstract class GenICode extends SubComponent { buf.toString() } - /** PP: This instruction was only emitted when settings.Xdce.value = true, - * but I don't understand the condition. It seems like the exception handler - * stacks won't balance without it, so put it in under -Ycheck too. - */ - def maybeLoadException(ctx: Context) = { - if (settings.Xdce.value || !settings.check.isDefault) - ctx.bb.emit(LOAD_EXCEPTION()) + def loadException(ctx: Context, exh: ExceptionHandler, pos: Position) = { + log("Emitting LOAD_EXCEPTION for class: " + exh.loadExceptionClass) + ctx.bb.emit(LOAD_EXCEPTION(exh.loadExceptionClass) setPos pos, pos) } def this(other: Context) = { @@ -1983,9 +1977,9 @@ abstract class GenICode extends SubComponent { * 'covered' by this exception handler (in addition to the * previously active handlers). */ - def newHandler(cls: Symbol, resultKind: TypeKind): ExceptionHandler = { + def newHandler(cls: Symbol, resultKind: TypeKind, pos: Position): ExceptionHandler = { handlerCount += 1 - val exh = new ExceptionHandler(method, "" + handlerCount, cls) + val exh = new ExceptionHandler(method, "" + handlerCount, cls, pos) exh.resultKind = resultKind method.addHandler(exh) handlers = exh :: handlers @@ -2093,15 +2087,15 @@ abstract class GenICode extends SubComponent { val finalizerExh = if (finalizer != EmptyTree) Some({ - val exh = outerCtx.newHandler(NoSymbol, toTypeKind(finalizer.tpe)) // finalizer covers exception handlers + val exh = outerCtx.newHandler(NoSymbol, toTypeKind(finalizer.tpe), finalizer.pos) // finalizer covers exception handlers this.addActiveHandler(exh) // .. and body aswell val ctx = finalizerCtx.enterHandler(exh) val exception = ctx.makeLocal(finalizer.pos, ThrowableClass.tpe, "exc") - maybeLoadException(ctx) + loadException(ctx, exh, finalizer.pos) ctx.bb.emit(STORE_LOCAL(exception)); val ctx1 = genLoad(finalizer, ctx, UNIT); ctx1.bb.emit(LOAD_LOCAL(exception)); - ctx1.bb.emit(THROW()); + ctx1.bb.emit(THROW(ThrowableClass)); ctx1.bb.enterIgnoreMode; ctx1.bb.close finalizerCtx.endHandler() @@ -2109,9 +2103,9 @@ abstract class GenICode extends SubComponent { }) else None val exhs = handlers.map { handler => - val exh = this.newHandler(handler._1, handler._2) + val exh = this.newHandler(handler._1, handler._2, tree.pos) var ctx1 = outerCtx.enterHandler(exh) - maybeLoadException(ctx1) + loadException(ctx1, exh, tree.pos) ctx1 = handler._3(ctx1) // emit finalizer val ctx2 = emitFinalizer(ctx1) @@ -2162,10 +2156,10 @@ abstract class GenICode extends SubComponent { if (finalizer != EmptyTree) { // finalizer is covers try and all catch blocks, i.e. // try { try { .. } catch { ..} } finally { .. } - val exh = outerCtx.newHandler(NoSymbol, UNIT) + val exh = outerCtx.newHandler(NoSymbol, UNIT, tree.pos) this.addActiveHandler(exh) val ctx = finalizerCtx.enterHandler(exh) - maybeLoadException(ctx) + loadException(ctx, exh, tree.pos) val ctx1 = genLoad(finalizer, ctx, UNIT) // need jump for the ICode to be valid. MSIL backend will emit `Endfinally` instead. ctx1.bb.closeWith(JUMP(afterCtx.bb)) @@ -2173,9 +2167,9 @@ abstract class GenICode extends SubComponent { } for (handler <- handlers) { - val exh = this.newHandler(handler._1, handler._2) + val exh = this.newHandler(handler._1, handler._2, tree.pos) var ctx1 = outerCtx.enterHandler(exh) - maybeLoadException(ctx1) + loadException(ctx1, exh, tree.pos) ctx1 = handler._3(ctx1) // msil backend will emit `Leave` to jump out of a handler ctx1.bb.closeWith(JUMP(afterCtx.bb)) diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala index 78862a90cb..8e92beebc3 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala @@ -59,15 +59,24 @@ abstract class ICodeCheckers { var method: IMethod = _ var code: Code = _ - val in: Map[BasicBlock, TypeStack] = new HashMap() + val in: Map[BasicBlock, TypeStack] = new HashMap() val out: Map[BasicBlock, TypeStack] = new HashMap() + val emptyStack = new TypeStack() { + override def toString = "<empty>" + } - val emptyStack = new TypeStack() - - val STRING = REFERENCE(definitions.StringClass) - val BOXED_UNIT = REFERENCE(definitions.BoxedUnitClass) - val SCALA_NOTHING = REFERENCE(definitions.NothingClass) - val SCALA_NULL = REFERENCE(definitions.NullClass) + /** The presence of emptyStack means that path has not yet been checked + * (and may not be empty). + */ + def notChecked(ts: TypeStack) = ts eq emptyStack + def initMaps(bs: Seq[BasicBlock]): Unit = { + in.clear() + out.clear() + bs foreach { b => + in(b) = emptyStack + out(b) = emptyStack + } + } /** A wrapper to route log messages to debug output also. */ @@ -83,17 +92,13 @@ abstract class ICodeCheckers { } private def posStr(p: Position) = - if (p.isDefined) p.line.toString else "<no pos>" + if (p.isDefined) p.line.toString else "<??>" private def indent(s: String, spaces: Int): String = indent(s, " " * spaces) private def indent(s: String, prefix: String): String = { val lines = s split "\\n" lines map (prefix + _) mkString "\n" } - private def blockAsString(bl: BasicBlock) = { - val s = bl.toList map (instr => posStr(instr.pos) + "\t" + instr) mkString (bl.fullString + " {\n ", "\n ", "\n}") - indent(s, "// ") - } /** Only called when m1 < m2, so already known that (m1 ne m2). */ @@ -103,7 +108,7 @@ abstract class ICodeCheckers { ) def check(cls: IClass) { - logChecker("\n** Checking class " + cls) + logChecker("\n<<-- Checking class " + cls + " -->>") clasz = cls for (f1 <- cls.fields ; f2 <- cls.fields ; if f1 < f2) @@ -118,36 +123,27 @@ abstract class ICodeCheckers { } def check(m: IMethod) { - logChecker("\n** Checking method " + m) + logChecker("\n<< Checking method " + m.symbol.name + " >>") method = m if (!m.isAbstractMethod) check(m.code) } def check(c: Code) { - var worklist: Buffer[BasicBlock] = new ListBuffer() + val worklist: Buffer[BasicBlock] = new ListBuffer() + def append(elems: List[BasicBlock]) = + worklist ++= (elems filterNot (worklist contains _)) - def append(elems: List[BasicBlock]) = elems foreach appendBlock; - def appendBlock(bl: BasicBlock) = - if (!(worklist contains bl)) - worklist += bl - - in.clear; - out.clear; - code = c; + code = c worklist += c.startBlock - for (bl <- c.blocks) { - in += (bl -> emptyStack) - out += (bl -> emptyStack) - } + initMaps(c.blocks) while (worklist.nonEmpty) { - val block = worklist(0); - worklist.trimStart(1); - val output = check(block, in(block)); - if (output != out(block) || (out(block) eq emptyStack)) { - if (block.successors.nonEmpty || block.successors.nonEmpty) - logChecker("Output changed for " + block.fullString) + val block = worklist remove 0 + val output = check(block, in(block)) + if (output != out(block) || notChecked(out(block))) { + if (block.successors.nonEmpty) + logChecker("** Output change for %s: %s -> %s".format(block, out(block), output)) out(block) = output append(block.successors) @@ -164,14 +160,21 @@ abstract class ICodeCheckers { def meet(bl: BasicBlock) { val preds = bl.predecessors + def hasNothingType(s: TypeStack) = s.nonEmpty && (s.head == NothingReference) + def hasNullType(s: TypeStack) = s.nonEmpty && (s.head == NullReference) + /** XXX workaround #1: one stack empty, the other has BoxedUnit. * One example where this arises is: * * def f(b: Boolean): Unit = synchronized { if (b) () } */ - def isAllUnits(s1: TypeStack, s2: TypeStack) = { - List(s1, s2) forall (x => x.types forall (_ == BOXED_UNIT)) + def allUnits(s: TypeStack) = s.types forall (_ == BoxedUnitReference) + + def ifAthenB[T](f: T => Boolean): PartialFunction[(T, T), T] = { + case (x1, x2) if f(x1) => x2 + case (x1, x2) if f(x2) => x1 } + /** XXX workaround #2: different stacks heading into an exception * handler which will clear them anyway. Examples where it arises: * @@ -179,9 +182,6 @@ abstract class ICodeCheckers { */ def isHandlerBlock() = bl.exceptionHandlerStart - /** The presence of emptyStack means that path has not yet been checked - * (and may not be empty) thus the reference eq tests. - */ def meet2(s1: TypeStack, s2: TypeStack): TypeStack = { def workaround(msg: String) = { checkerDebug(msg + ": " + method + " at block " + bl) @@ -190,27 +190,34 @@ abstract class ICodeCheckers { new TypeStack() } def incompatibleString = ( - "Incompatible stacks: " + s1 + " and " + s2 + " in " + method + " at entry to:\n" + - blockAsString(bl) + "Incompatible stacks: " + s1 + " and " + s2 + " in " + method + " at entry to block " + bl.label + ":\n" + + indent(bl.predContents, "// ") + + indent(bl.succContents, "// ") + + indent(bl.blockContents, "// ") ) - if (s1 eq emptyStack) s2 - else if (s2 eq emptyStack) s1 - else if (s1.length != s2.length) { - if (isAllUnits(s1, s2)) - workaround("Ignoring mismatched boxed units") - else if (isHandlerBlock) - workaround("Ignoring mismatched stacks entering exception handler") - else - throw new CheckerException(incompatibleString) + val f: ((TypeStack, TypeStack)) => TypeStack = { + ifAthenB(notChecked) orElse ifAthenB(hasNothingType) orElse { + case (s1: TypeStack, s2: TypeStack) => + if (s1.length != s2.length) { + if (allUnits(s1) && allUnits(s2)) + workaround("Ignoring mismatched boxed units") + else if (isHandlerBlock) + workaround("Ignoring mismatched stacks entering exception handler") + else + throw new CheckerException(incompatibleString) + } + else { + val newStack = new TypeStack((s1.types, s2.types).zipped map lub) + if (newStack.isEmpty || s1.types == s2.types) () // not interesting to report + else checkerDebug("Checker created new stack:\n (%s, %s) => %s".format(s1, s2, newStack)) + + newStack + } + } } - else { - val newStack = new TypeStack((s1.types, s2.types).zipped map lub) - if (newStack.nonEmpty) - checkerDebug("Checker created new stack:\n (%s, %s) => %s".format(s1, s2, newStack)) - newStack - } + f((s1, s2)) } if (preds.nonEmpty) { @@ -221,12 +228,52 @@ abstract class ICodeCheckers { private var instruction: Instruction = null private var basicBlock: BasicBlock = null + private var stringConcatDepth = 0 + private def stringConcatIndent() = " " * stringConcatDepth + private def currentInstrString: String = { + val (indent, str) = this.instruction match { + case CALL_PRIMITIVE(StartConcat) => + val x = stringConcatIndent() + stringConcatDepth += 1 + (x, "concat(") + case CALL_PRIMITIVE(EndConcat) => + if (stringConcatDepth > 0) { + stringConcatDepth -= 1 + (stringConcatIndent(), ") // end concat") + } + else ("", "") + case _ => + (stringConcatIndent(), this.instruction match { + case CALL_PRIMITIVE(StringConcat(el)) => "..." + case null => "null" + case cm @ CALL_METHOD(_, _) => if (clasz.symbol == cm.hostClass) cm.toShortString else cm.toString + case x => x.toString + }) + } + indent + str + } + /** A couple closure creators to reduce noise in the output: when multiple + * items are pushed or popped, this lets us print something short and sensible + * for those beyond the first. + */ + def mkInstrPrinter(f: Int => String): () => String = { + var counter = -1 + val indent = stringConcatIndent() + () => { + counter += 1 + if (counter == 0) currentInstrString + else indent + f(counter) + } + } + def defaultInstrPrinter: () => String = mkInstrPrinter(_ => "\"\"\"") /** * Check the basic block to be type correct and return the * produced type stack. */ def check(b: BasicBlock, initial: TypeStack): TypeStack = { + this.basicBlock = b + logChecker({ val prefix = "** Checking " + b.fullString @@ -246,70 +293,97 @@ abstract class ICodeCheckers { sp + stack.length + arrow } + def printStackString(isPush: Boolean, value: TypeKind, instrString: String) = { + val pushString = if (isPush) "+" else "-" + val posString = posStr(this.instruction.pos) + + checkerDebug("%-70s %-4s %s %s".format(sizeString(isPush) + value, posString, pushString, instrString)) + } def _popStack: TypeKind = { if (stack.isEmpty) { error("Popped empty stack in " + b.fullString + ", throwing a Unit") return UNIT } - - val res = stack.pop - checkerDebug(sizeString(false) + res) - res + stack.pop } - def popStack = { checkStack(1) ; _popStack } - def popStack2 = { checkStack(2) ; (_popStack, _popStack) } - def popStack3 = { checkStack(3) ; (_popStack, _popStack, _popStack) } - def clearStack() = 1 to stack.length foreach (_ => popStack) - - def pushStack(xs: TypeKind*): Unit = { + def popStackN(num: Int, instrFn: () => String = defaultInstrPrinter) = { + List.range(0, num) map { _ => + val res = _popStack + printStackString(false, res, instrFn()) + res + } + } + def pushStackN(xs: Seq[TypeKind], instrFn: () => String) = { xs foreach { x => - if (x == UNIT) { - /** PP: I admit I'm not yet figuring out how the stacks will balance when - * we ignore UNIT, but I expect this knowledge will emerge naturally. - * In the meantime I'm logging it to help me out. - */ - logChecker("Ignoring pushed UNIT") - } - else { - stack push x - checkerDebug(sizeString(true) + x) - } + stack push x + printStackString(true, x, instrFn()) } } - this.basicBlock = b + def popStack = { checkStack(1) ; popStackN(1) match { case List(x) => x } } + def popStack2 = { checkStack(2) ; popStackN(2) match { case List(x, y) => (x, y) } } + def popStack3 = { checkStack(3) ; popStackN(3) match { case List(x, y, z) => (x, y, z) } } + + /** Called by faux instruction LOAD_EXCEPTION to wipe out the stack. */ + def clearStack() = { + if (stack.nonEmpty) + logChecker("Wiping out the " + stack.length + " element stack for exception handler: " + stack) + + 1 to stack.length foreach (_ => popStack) + } + + def pushStack(xs: TypeKind*): Unit = { + pushStackN(xs filterNot (_ == UNIT), defaultInstrPrinter) + } def typeError(k1: TypeKind, k2: TypeKind) { error("\n expected: " + k1 + "\n found: " + k2) } + def isSubtype(k1: TypeKind, k2: TypeKind) = (k1 <:< k2) || { + import platform.isMaybeBoxed + + (k1, k2) match { + case (REFERENCE(_), REFERENCE(_)) if k1.isInterfaceType || k2.isInterfaceType => + logChecker("Considering %s <:< %s because at least one is an interface".format(k1, k2)) + true + case (REFERENCE(cls1), REFERENCE(cls2)) if isMaybeBoxed(cls1) || isMaybeBoxed(cls2) => + logChecker("Considering %s <:< %s because at least one might be a boxed primitive".format(cls1, cls2)) + true + case _ => + false + } + } + + /** Return true if k1 is a subtype of any of the following types, + * according to the somewhat relaxed subtyping standards in effect here. + */ + def isOneOf(k1: TypeKind, kinds: TypeKind*) = kinds exists (k => isSubtype(k1, k)) def subtypeTest(k1: TypeKind, k2: TypeKind): Unit = - if (k1 <:< k2) () + if (isSubtype(k1, k2)) () else typeError(k2, k1) for (instr <- b) { + this.instruction = instr def checkLocal(local: Local): Unit = { - (method lookupLocal local.sym.name) getOrElse { + method lookupLocal local.sym.name getOrElse { error(" " + local + " is not defined in method " + method) } } - - def checkField(obj: TypeKind, field: Symbol) { - obj match { - case REFERENCE(sym) => - if (sym.info.member(field.name) == NoSymbol) - error(" " + field + " is not defined in class " + clasz); - case _ => - error(" expected reference type, but " + obj + " found"); - } + def checkField(obj: TypeKind, field: Symbol): Unit = obj match { + case REFERENCE(sym) => + if (sym.info.member(field.name) == NoSymbol) + error(" " + field + " is not defined in class " + clasz); + case _ => + error(" expected reference type, but " + obj + " found"); } /** Checks that tpe is a subtype of one of the allowed types */ - def checkType(tpe: TypeKind, allowed: TypeKind*) { - if (isOneOf(tpe, allowed: _*)) () + def checkType(tpe: TypeKind, allowed: TypeKind*) = ( + if (allowed exists (k => isSubtype(tpe, k))) () else error(tpe + " is not one of: " + allowed.mkString("{ ", ", ", " }")) - } + ) def checkNumeric(tpe: TypeKind) = checkType(tpe, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE) @@ -326,7 +400,10 @@ abstract class ICodeCheckers { def checkMethodArgs(method: Symbol) { val params = method.info.paramTypes checkStack(params.length) - params.reverse foreach (tpe => checkType(popStack, toTypeKind(tpe))) + ( + popStackN(params.length, mkInstrPrinter(num => "<arg" + num + ">")), + params.reverse map toTypeKind).zipped foreach ((x, y) => checkType(x, y) + ) } /** Checks that the object passed as receiver has a method @@ -344,10 +421,16 @@ abstract class ICodeCheckers { checkBool(method.owner == clasz.symbol, "Cannot call private method of " + method.owner.fullName + " from " + clasz.symbol.fullName); - else if (method.isProtected) - checkBool(clasz.symbol isSubClass method.owner, + else if (method.isProtected) { + val isProtectedOK = ( + (clasz.symbol isSubClass method.owner) || + (clasz.symbol.typeOfThis.typeSymbol isSubClass method.owner) // see pos/bug780.scala + ) + + checkBool(isProtectedOK, "Cannot call protected method of " + method.owner.fullName + " from " + clasz.symbol.fullName); + } case ARRAY(_) => checkBool(receiver.toType.member(method.name) != NoSymbol, @@ -360,8 +443,6 @@ abstract class ICodeCheckers { def checkBool(cond: Boolean, msg: String) = if (!cond) error(msg) - this.instruction = instr - if (settings.debug.value) { log("PC: " + instr) log("stack: " + stack) @@ -420,10 +501,8 @@ abstract class ICodeCheckers { case STORE_LOCAL(local) => checkLocal(local) val actualType = popStack - // PP: ThrowableReference is temporary to deal with exceptions - // not yet appearing typed. - if (actualType == ThrowableReference || local.kind == SCALA_NULL) () - else subtypeTest(actualType, local.kind) + if (local.kind != NullReference) + subtypeTest(actualType, local.kind) case STORE_FIELD(field, true) => // static val fieldType = toTypeKind(field.tpe) @@ -434,7 +513,7 @@ abstract class ICodeCheckers { val (value, obj) = popStack2 checkField(obj, field) val fieldType = toTypeKind(field.tpe) - if (fieldType == SCALA_NULL) () + if (fieldType == NullReference) () else subtypeTest(value, fieldType) case CALL_PRIMITIVE(primitive) => @@ -494,7 +573,7 @@ abstract class ICodeCheckers { case EndConcat => checkType(popStack, ConcatClass) - pushStack(STRING) + pushStack(StringReference) case StringConcat(el) => checkType(popStack, el) @@ -503,38 +582,24 @@ abstract class ICodeCheckers { } case CALL_METHOD(method, style) => - def paramCount = method.info.paramTypes.length - def pushReturnType = pushStack(toTypeKind(method.info.resultType)) - - style match { - case Dynamic | InvokeDynamic => - checkStack(1 + paramCount) - checkMethodArgs(method) - checkMethod(popStack, method) - pushReturnType - - case Static(onInstance) => - if (onInstance) { - checkStack(1 + paramCount) - checkBool(method.isPrivate || method.isConstructor, - "Static call to non-private method.") - checkMethodArgs(method) - checkMethod(popStack, method) - if (!method.isConstructor) - pushReturnType - } - else { - checkStack(paramCount); - checkMethodArgs(method); - pushReturnType - } - - case SuperCall(mix) => - checkStack(1 + paramCount) - checkMethodArgs(method) - checkMethod(popStack, method) - pushReturnType + // PP to ID: I moved the if (!method.isConstructor) check to cover all + // the styles to address checker failure. Can you confirm if the change + // was correct? If I remember right it's a matter of whether some brand + // of supercall should leave a value on the stack, and I know there is some + // trickery performed elsewhere regarding this. + val paramCount = method.info.paramTypes.length match { + case x if style.hasInstance => x + 1 + case x => x } + if (style == Static(true)) + checkBool(method.isPrivate || method.isConstructor, "Static call to non-private method.") + + checkStack(paramCount) + checkMethodArgs(method) + if (style.hasInstance) + checkMethod(popStack, method) + if (!method.isConstructor) + pushStack(toTypeKind(method.info.resultType)) case NEW(kind) => pushStack(kind) @@ -587,11 +652,9 @@ abstract class ICodeCheckers { if (kind.isValueType) checkType(top, kind) else checkBool(!top.isValueType, "" + kind + " is a reference type, but " + top + " is not"); - case THROW() => - val thrown = popStack - checkBool(thrown.toType <:< definitions.ThrowableClass.tpe, - "Element on top of stack should implement 'Throwable': " + thrown); - pushStack(SCALA_NOTHING) + case THROW(clasz) => + checkType(popStack, toTypeKind(clasz.tpe)) + pushStack(NothingReference) case DROP(kind) => checkType(popStack, kind) @@ -610,15 +673,15 @@ abstract class ICodeCheckers { case BOX(kind) => checkType(popStack, kind) - pushStack(icodes.ObjectReference) + pushStack(REFERENCE(definitions.boxedClass(kind.toType.typeSymbol))) case UNBOX(kind) => popStack pushStack(kind) - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(clasz) => clearStack() - pushStack(ThrowableReference) + pushStack(REFERENCE(clasz)) case SCOPE_ENTER(_) | SCOPE_EXIT(_) => () @@ -633,18 +696,15 @@ abstract class ICodeCheckers { //////////////// Error reporting ///////////////////////// def error(msg: String) { - ICodeCheckers.this.global.error("!! ICode checker fatality in " + method + " at:" + blockAsString(basicBlock) + ":\n " + msg) + ICodeCheckers.this.global.error( + "!! ICode checker fatality in " + method + + "\n at: " + basicBlock.fullString + + "\n error message: " + msg + ) } def error(msg: String, stack: TypeStack) { error(msg + "\n type stack: " + stack) } - - //////////////////// Checking ///////////////////////////// - - /** Return true if <code>k1</code> is a subtype of any of the following - * types. - */ - def isOneOf(k1: TypeKind, kinds: TypeKind*) = kinds exists (k1 <:< _) } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index 65b558b230..dc31edc569 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -14,9 +14,9 @@ package icode import java.io.PrintWriter -import scala.collection.mutable.HashMap +import scala.collection.{ mutable, immutable, generic } import scala.tools.nsc.symtab._ -import analysis.{Liveness, ReachingDefinitions} +import analysis.{ Liveness, ReachingDefinitions } import scala.tools.nsc.symtab.classfile.ICodeReader /** Glue together ICode parts. @@ -36,26 +36,23 @@ abstract class ICodes extends AnyRef with Repository { val global: Global + import global.{ definitions, settings } /** The ICode representation of classes */ - var classes: HashMap[global.Symbol, IClass] = new HashMap() + val classes = new mutable.HashMap[global.Symbol, IClass] /** Debugging flag */ - var isCheckerDebug: Boolean = global.settings.checkDebug.value - def checkerDebug(msg: String) = if (isCheckerDebug) println(msg) + def shouldCheckIcode = settings.check contains global.genicode.phaseName + def checkerDebug(msg: String) = if (shouldCheckIcode && global.opt.debug) println(msg) /** The ICode linearizer. */ - val linearizer: Linearizer = - if (global.settings.Xlinearizer.value == "rpo") - new ReversePostOrderLinearizer() - else if (global.settings.Xlinearizer.value == "dfs") - new DepthFirstLinerizer() - else if (global.settings.Xlinearizer.value == "normal") - new NormalLinearizer(); - else if (global.settings.Xlinearizer.value == "dump") - new DumpLinearizer() - else - global.abort("Unknown linearizer: " + global.settings.Xlinearizer.value) + val linearizer: Linearizer = settings.Xlinearizer.value match { + case "rpo" => new ReversePostOrderLinearizer() + case "dfs" => new DepthFirstLinerizer() + case "normal" => new NormalLinearizer() + case "dump" => new DumpLinearizer() + case x => global.abort("Unknown linearizer: " + x) + } /** Have to be careful because dump calls around, possibly * re-entering methods which initiated the dump (like foreach @@ -79,7 +76,7 @@ abstract class ICodes extends AnyRef for (b <- m.code.blocks) if (!b.closed) { m.dump - global.abort("Open block: " + b.flagsString) + global.abort("Open block: " + b + " " + b.flagsString) } } @@ -91,9 +88,13 @@ abstract class ICodes extends AnyRef val global: ICodes.this.global.type = ICodes.this.global } - lazy val ObjectReference: TypeKind = REFERENCE(global.definitions.ObjectClass) - lazy val ThrowableReference: TypeKind = REFERENCE(global.definitions.ThrowableClass) - lazy val AnyRefReference: TypeKind = REFERENCE(global.definitions.AnyRefClass) + lazy val AnyRefReference: TypeKind = REFERENCE(definitions.AnyRefClass) + lazy val BoxedUnitReference: TypeKind = REFERENCE(definitions.BoxedUnitClass) + lazy val NothingReference: TypeKind = REFERENCE(definitions.NothingClass) + lazy val NullReference: TypeKind = REFERENCE(definitions.NullClass) + lazy val ObjectReference: TypeKind = REFERENCE(definitions.ObjectClass) + lazy val StringReference: TypeKind = REFERENCE(definitions.StringClass) + lazy val ThrowableReference: TypeKind = REFERENCE(definitions.ThrowableClass) object icodeReader extends ICodeReader { lazy val global: ICodes.this.global.type = ICodes.this.global @@ -102,10 +103,8 @@ abstract class ICodes extends AnyRef /** A phase which works on icode. */ abstract class ICodePhase(prev: Phase) extends global.GlobalPhase(prev) { override def erasedTypes = true - - override def apply(unit: global.CompilationUnit) { - unit.icode foreach { c => apply(c) } - } + override def apply(unit: global.CompilationUnit): Unit = + unit.icode foreach apply def apply(cls: global.icodes.IClass): Unit } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala index 701e5d3815..9bb704f8b7 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala @@ -73,7 +73,7 @@ trait Linearizers { self: ICodes => case SWITCH(_, labels) => add(labels); case RETURN(_) => (); - case THROW() => (); + case THROW(clasz) => (); } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 6c3ec7fc19..5ee4245e35 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -36,7 +36,7 @@ import scala.tools.nsc.util.{Position,NoPosition} case CJUMP(success, failure, cond, kind) => case CZJUMP(success, failure, cond, kind) => case RETURN(kind) => - case THROW() => + case THROW(clasz) => case DROP(kind) => case DUP(kind) => case MONITOR_ENTER() => @@ -45,7 +45,7 @@ import scala.tools.nsc.util.{Position,NoPosition} case UNBOX(tpe) => case SCOPE_ENTER(lv) => case SCOPE_EXIT(lv) => - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(clasz) => */ @@ -102,7 +102,7 @@ trait Opcodes { self: ICodes => def mayThrow(i: Instruction): Boolean = i match { case LOAD_LOCAL(_) | STORE_LOCAL(_) | CONSTANT(_) | THIS(_) | CZJUMP(_, _, _, _) - | DROP(_) | DUP(_) | RETURN(_) | LOAD_EXCEPTION() | JUMP(_) | CJUMP(_, _, _, _) => false + | DROP(_) | DUP(_) | RETURN(_) | LOAD_EXCEPTION(_) | JUMP(_) | CJUMP(_, _, _, _) => false case _ => true } @@ -125,6 +125,7 @@ trait Opcodes { self: ICodes => * ->: ...:constant */ case class CONSTANT(constant: Constant) extends Instruction { + override def toString = "CONSTANT(" + constant.escapedStringValue + ")" override def consumed = 0 override def produced = 1 @@ -299,6 +300,9 @@ trait Opcodes { self: ICodes => * */ case class CALL_METHOD(method: Symbol, style: InvokeStyle) extends Instruction with ReferenceEquality { + def toShortString = + "CALL_METHOD " + method.name +" ("+style+")" + /** Returns a string representation of this instruction */ override def toString(): String = "CALL_METHOD " + method.fullName +" ("+style+")" @@ -492,9 +496,12 @@ trait Opcodes { self: ICodes => * Stack: ...:Throwable(Ref) * ->: ...: */ - case class THROW() extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String ="THROW" + case class THROW(clasz: Symbol) extends Instruction { + /** PP to ID: We discussed parameterizing LOAD_EXCEPTION but + * not THROW, which came about organically. It seems like the + * right thing, but can you confirm? + */ + override def toString = "THROW(" + clasz.name + ")" override def consumed = 1 override def produced = 0 @@ -570,11 +577,10 @@ trait Opcodes { self: ICodes => * Note: Unlike other instructions, it consumes all elements on the stack! * then pushes one exception instance. */ - case class LOAD_EXCEPTION() extends Instruction { - override def toString(): String = "LOAD_EXCEPTION" + case class LOAD_EXCEPTION(clasz: Symbol) extends Instruction { override def consumed = error("LOAD_EXCEPTION does clean the whole stack, no idea how many things it consumes!") override def produced = 1 - override def producedTypes = ThrowableReference :: Nil + override def producedTypes = REFERENCE(clasz) :: Nil } /** This class represents a method invocation style. */ @@ -598,10 +604,8 @@ trait Opcodes { self: ICodes => /** Is this an instance method call? */ def hasInstance: Boolean = this match { - case Dynamic => true - case Static(onInstance) => onInstance - case SuperCall(_) => true - case _ => false + case Static(false) => false + case _ => true } /** Returns a string representation of this style. */ diff --git a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala index 4fbec28fdf..edb42cb3fe 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala @@ -125,7 +125,7 @@ trait Printers { self: ICodes => def printInstruction(i: Instruction) { // if (settings.Xdce.value) // print(if (i.useful) " " else " * "); - if (i.pos.isDefined) print(i.pos.line.toString + "\t") else print("undef\t") + if (i.pos.isDefined) print(i.pos.line.toString + "\t") else print("?\t") println(i.toString()) } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index 8909171587..da6625d037 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -26,7 +26,7 @@ package icode trait TypeKinds { self: ICodes => import global._ import definitions.{ ArrayClass, AnyRefClass, ObjectClass, NullClass, NothingClass, arrayType } - import icodes.checkerDebug + import icodes.{ checkerDebug, NothingReference, NullReference } /** A map from scala primitive Types to ICode TypeKinds */ lazy val primitiveTypeMap: Map[Symbol, TypeKind] = { @@ -68,12 +68,12 @@ trait TypeKinds { self: ICodes => def isBoxedType = false final def isRefOrArrayType = isReferenceType || isArrayType final def isRefArrayOrBoxType = isRefOrArrayType || isBoxedType - final def isNothingType = this == REFERENCE(NothingClass) + final def isNothingType = this == NothingReference + final def isNullType = this == NullReference final def isInterfaceType = this match { - case REFERENCE(cls) if cls.isInterface => true - case _ => false + case REFERENCE(cls) if cls.isInterface || cls.isTrait => true + case _ => false } - final def isBoxOrInterfaceType = isBoxedType || isInterfaceType /** On the JVM, these types are like Ints for the * purposes of calculating the lub. @@ -150,21 +150,15 @@ trait TypeKinds { self: ICodes => import definitions._ val tp = global.lub(List(tk1.toType, tk2.toType)) val (front, rest) = tp.parents span (_.typeSymbol.hasTraitFlag) - if (front.isEmpty || rest.isEmpty) tp + + if (front.isEmpty) tp + else if (rest.isEmpty) front.head // all parents are interfaces else rest.head match { - case AnyClass | AnyRefClass | AnyValClass => tp - case x => x + case AnyRefClass | ObjectClass => tp + case x => x } } - // Approximate the JVM view of subtyping by collapsing boxed - // values and interfaces into AnyRef. If we try to be more precise - // with intersections we run into post-erasure issues where a type - // like A with B may be used as an A in one place and a B in another. - def isBoxLub = ( - (a.isBoxOrInterfaceType && b.isRefArrayOrBoxType) || - (b.isBoxOrInterfaceType && a.isRefArrayOrBoxType) - ) def isIntLub = ( (a == INT && b.isIntSizedType) || (b == INT && a.isIntSizedType) @@ -173,9 +167,13 @@ trait TypeKinds { self: ICodes => if (a == b) a else if (a.isNothingType) b else if (b.isNothingType) a - else if (isBoxLub) AnyRefReference + else if (a.isBoxedType || b.isBoxedType) AnyRefReference // we should do better else if (isIntLub) INT - else if (a.isRefOrArrayType && b.isRefOrArrayType) toTypeKind(lub0(a, b)) + else if (a.isRefOrArrayType && b.isRefOrArrayType) { + if (a.isNullType) b + else if (b.isNullType) a + else toTypeKind(lub0(a, b)) + } else throw new CheckerException("Incompatible types: " + a + " with " + b) } @@ -331,19 +329,16 @@ trait TypeKinds { self: ICodes => /** A boxed value. */ case class BOXED(kind: TypeKind) extends TypeKind { override def isBoxedType = true - /** - * Approximate `lub'. The common type of two references is - * always AnyRef. For 'real' least upper bound wrt to subclassing - * use method 'lub'. - */ + override def maxType(other: TypeKind) = other match { + case BOXED(`kind`) => this case REFERENCE(_) | ARRAY(_) | BOXED(_) => AnyRefReference case _ => uncomparable("BOXED", other) } /** Checks subtyping relationship. */ override def <:<(other: TypeKind) = other match { - case BOXED(other) => kind == other + case BOXED(`kind`) => true case REFERENCE(AnyRefClass | ObjectClass) => true // TODO: platform dependent! case _ => false } @@ -387,6 +382,9 @@ trait TypeKinds { self: ICodes => case ClassInfoType(_, _, sym) => primitiveOrRefType(sym) case ExistentialType(_, t) => toTypeKind(t) case AnnotatedType(_, t, _) => toTypeKind(t) + // PP to ID: I added RefinedType here, is this OK or should they never be + // allowed to reach here? + case RefinedType(parents, _) => parents map toTypeKind reduceLeft lub // bq: useful hack when wildcard types come here // case WildcardType => REFERENCE(ObjectClass) case norm => abort( @@ -405,12 +403,23 @@ trait TypeKinds { self: ICodes => assert(sym.isType, sym) // it must be compiling Array[a] ObjectReference } - /** Interfaces are all treated as AnyRef else we will run into - * post-erasure inconsistencies when differing lubs are needed. + /** Interfaces have to be handled delicately to avoid introducing + * spurious errors, but if we treat them all as AnyRef we lose too + * much information. */ - private def newReference(sym: Symbol) = - if (sym.isInterface || sym.isTrait) ObjectReference - else REFERENCE(sym) + private def newReference(sym: Symbol): TypeKind = { + // Can't call .toInterface (at this phase) or we trip an assertion. + // See PackratParser#grow for a method which fails with an apparent mismatch + // between "object PackratParsers$class" and "trait PackratParsers" + if (sym.isImplClass) { + // pos/spec-List.scala is the sole failure if we don't check for NoSymbol + val traitSym = sym.owner.info.decl(nme.interfaceName(sym.name)) + if (traitSym != NoSymbol) + return REFERENCE(traitSym) + } + REFERENCE(sym) + } + private def primitiveOrRefType(sym: Symbol) = primitiveTypeMap.getOrElse(sym, newReference(sym)) private def primitiveOrClassType(sym: Symbol, targs: List[Type]) = diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala index b026921ca6..5ac44380f2 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala @@ -406,7 +406,7 @@ abstract class CopyPropagation { if (kind != UNIT) out.stack = out.stack.drop(1) - case THROW() => + case THROW(_) => out.stack = out.stack.drop(1) case DROP(kind) => @@ -424,7 +424,7 @@ abstract class CopyPropagation { case SCOPE_ENTER(_) | SCOPE_EXIT(_) => () - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(_) => out.stack = Unknown :: Nil case _ => diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala index c0de380b02..d7e5e84a80 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala @@ -121,15 +121,15 @@ abstract class ReachingDefinitions { var stackOut: List[Set[(BasicBlock, Int)]] = Nil for ((instr, idx) <- b.toList.zipWithIndex) { - if (instr == LOAD_EXCEPTION()) - () - else if (instr.consumed > depth) { - drops = drops + (instr.consumed - depth) - depth = 0 - stackOut = Nil - } else { - stackOut = stackOut.drop(instr.consumed) - depth = depth - instr.consumed + instr match { + case LOAD_EXCEPTION(_) => () + case _ if instr.consumed > depth => + drops += (instr.consumed - depth) + depth = 0 + stackOut = Nil + case _ => + stackOut = stackOut.drop(instr.consumed) + depth -= instr.consumed } var prod = instr.produced depth = depth + prod @@ -178,7 +178,7 @@ abstract class ReachingDefinitions { case STORE_LOCAL(l1) => locals = updateReachingDefinition(b, idx, locals) stack = stack.drop(instr.consumed) - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(_) => stack = Nil case _ => stack = stack.drop(instr.consumed) @@ -211,8 +211,9 @@ abstract class ReachingDefinitions { if (prod > d) { res = (bb, i) :: res n = n - (prod - d) - if (instrs(i) != LOAD_EXCEPTION()) { - d = instrs(i).consumed + instrs(i) match { + case LOAD_EXCEPTION(_) => () + case _ => d = instrs(i).consumed } } else { d -= prod diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala index b26b799f6d..ec36e50205 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala @@ -16,7 +16,7 @@ import scala.collection.{mutable, immutable} abstract class TypeFlowAnalysis { val global: Global import global._ - import definitions.{ ObjectClass, NothingClass, AnyRefClass, StringClass } + import definitions.{ ObjectClass, NothingClass, AnyRefClass, StringClass, ThrowableClass } /** The lattice of ICode types. */ @@ -39,9 +39,9 @@ abstract class TypeFlowAnalysis { import icodes._ type Elem = TypeStack - val top = new TypeStack - val bottom = new TypeStack - val exceptionHandlerStack: TypeStack = new TypeStack(List(REFERENCE(AnyRefClass))) + val top = new TypeStack + val bottom = new TypeStack + val exceptionHandlerStack = new TypeStack(List(REFERENCE(AnyRefClass))) def lub2(exceptional: Boolean)(s1: TypeStack, s2: TypeStack) = { if (s1 eq bottom) s2 @@ -144,9 +144,12 @@ abstract class TypeFlowAnalysis { */ } out(b) = typeFlowLattice.bottom } - m.exh map (_.startBlock) filterNot (in contains _) foreach { start => - worklist += start - in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack) + for (handler <- m.exh) { + val start = handler.startBlock + if (!in.contains(start)) { + worklist += start + in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack) + } } } } @@ -351,7 +354,7 @@ abstract class TypeFlowAnalysis { case SCOPE_ENTER(_) | SCOPE_EXIT(_) => () - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(_) => stack.pop(stack.length) stack.push(typeLattice.Object) @@ -517,7 +520,7 @@ abstract class TypeFlowAnalysis { if (kind != UNIT) stack.pop; - case THROW() => + case THROW(_) => stack.pop case DROP(kind) => @@ -535,7 +538,7 @@ abstract class TypeFlowAnalysis { case SCOPE_ENTER(_) | SCOPE_EXIT(_) => () - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(_) => stack.pop(stack.length) stack.push(typeLattice.top) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 1c0d80b0c7..160ce03a7f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -1393,7 +1393,7 @@ abstract class GenJVM extends SubComponent { case RETURN(kind) => jcode emitRETURN javaType(kind) - case THROW() => + case THROW(_) => jcode.emitATHROW() case DROP(kind) => @@ -1429,7 +1429,7 @@ abstract class GenJVM extends SubComponent { } else assert(false, "Illegal local var nesting: " + method) - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(_) => () } diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index ad60524485..4268d999a0 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -809,7 +809,7 @@ abstract class GenMSIL extends SubComponent { NEW(REFERENCE(definitions.ThrowableClass)), DUP(REFERENCE(definitions.ObjectClass)), CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)), - THROW() + THROW(definitions.ThrowableClass) )) b.close endExBlock(b) = rest @@ -1321,7 +1321,7 @@ abstract class GenMSIL extends SubComponent { mcode.Emit(OpCodes.Leave, label) } - case THROW() => + case THROW(_) => mcode.Emit(OpCodes.Throw) case DROP(kind) => @@ -1340,7 +1340,7 @@ abstract class GenMSIL extends SubComponent { case MONITOR_EXIT() => mcode.Emit(OpCodes.Call, MMONITOR_EXIT) - case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION() => + case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION(_) => () } diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 43eebcb0ad..6f8448ad0a 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -103,8 +103,8 @@ abstract class DeadCodeElimination extends SubComponent { defs = defs + Pair(((bb, idx)), rd.vars) // Console.println(i + ": " + (bb, idx) + " rd: " + rd + " and having: " + defs) case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | - THROW() | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | - LOAD_EXCEPTION() | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx)) + THROW(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | + LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx)) case CALL_METHOD(m1, _) if isSideEffecting(m1) => worklist += ((bb, idx)); log("marking " + m1) case CALL_METHOD(m1, SuperCall(_)) => worklist += ((bb, idx)) // super calls to constructor @@ -113,7 +113,7 @@ abstract class DeadCodeElimination extends SubComponent { val (bb1, idx1) = p bb1(idx1) match { case CALL_METHOD(m1, _) if isSideEffecting(m1) => true - case LOAD_EXCEPTION() | DUP(_) | LOAD_MODULE(_) => true + case LOAD_EXCEPTION(_) | DUP(_) | LOAD_MODULE(_) => true case _ => dropOf((bb1, idx1)) = (bb, idx) // println("DROP is innessential: " + i + " because of: " + bb1(idx1) + " at " + bb1 + ":" + idx1) @@ -168,7 +168,7 @@ abstract class DeadCodeElimination extends SubComponent { log("added closure class for field " + sym) liveClosures += sym.owner - case LOAD_EXCEPTION() => + case LOAD_EXCEPTION(_) => () case _ => diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index c15a4279fb..a59bc776aa 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -8,7 +8,7 @@ package scala.tools package nsc package settings -import io.{AbstractFile, VirtualDirectory} +import io.{ AbstractFile, VirtualDirectory } import scala.tools.util.StringOps import scala.collection.mutable.ListBuffer import scala.io.Source diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 7156da076f..be6d886537 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -106,7 +106,6 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings { val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options") val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") - val checkDebug = BooleanSetting ("-Ycheck-debug", "Lots of extra output for -Ycheck") val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination") val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees") val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL") diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 6a9cdf6961..ef80177464 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -10,6 +10,7 @@ import scala.collection.mutable.{HashMap, HashSet} import scala.tools.nsc.util.NoPosition import Flags._ import PartialFunction._ +import classfile.ClassfileConstants trait Definitions extends reflect.generic.StandardDefinitions { self: SymbolTable => @@ -71,17 +72,17 @@ trait Definitions extends reflect.generic.StandardDefinitions { lazy val ObjectClass = getClass(sn.Object) // bottom types - lazy val NullClass = newClass(ScalaPackageClass, nme.Null, anyrefparam) setFlag (ABSTRACT | TRAIT | FINAL) - lazy val NothingClass = newClass(ScalaPackageClass, nme.Nothing, anyparam) setFlag (ABSTRACT | TRAIT | FINAL) - lazy val RuntimeNothingClass = getClass("scala.runtime.Nothing$") - lazy val RuntimeNullClass = getClass("scala.runtime.Null$") + lazy val NullClass = newClass(ScalaPackageClass, nme.Null, anyrefparam) setFlag (ABSTRACT | TRAIT | FINAL) + lazy val NothingClass = newClass(ScalaPackageClass, nme.Nothing, anyparam) setFlag (ABSTRACT | TRAIT | FINAL) + lazy val RuntimeNothingClass = getClass(ClassfileConstants.SCALA_NOTHING) + lazy val RuntimeNullClass = getClass(ClassfileConstants.SCALA_NULL) lazy val AnyValCompanionClass = getClass("scala.runtime.AnyValCompanion").setFlag(SEALED | ABSTRACT | TRAIT) // the scala value classes lazy val UnitClass = newClass(ScalaPackageClass, nme.Unit, anyvalparam).setFlag(ABSTRACT | FINAL) - import classfile.ClassfileConstants._ + import ClassfileConstants._ lazy val ByteClass = newValueClass(nme.Byte, BYTE_TAG, 2) lazy val ShortClass = newValueClass(nme.Short, SHORT_TAG, 4) @@ -107,13 +108,14 @@ trait Definitions extends reflect.generic.StandardDefinitions { ) // exceptions and other throwables - lazy val ThrowableClass = getClass(sn.Throwable) - lazy val NullPointerExceptionClass = getClass(sn.NPException) - lazy val NonLocalReturnControlClass = getClass(sn.NLRControl) + lazy val ClassCastExceptionClass = getClass("java.lang.ClassCastException") lazy val IndexOutOfBoundsExceptionClass = getClass(sn.IOOBException) - lazy val UninitializedErrorClass = getClass("scala.UninitializedFieldError") - lazy val MatchErrorClass = getClass("scala.MatchError") lazy val InvocationTargetExceptionClass = getClass(sn.InvTargetException) + lazy val MatchErrorClass = getClass("scala.MatchError") + lazy val NonLocalReturnControlClass = getClass(sn.NLRControl) + lazy val NullPointerExceptionClass = getClass(sn.NPException) + lazy val ThrowableClass = getClass(sn.Throwable) + lazy val UninitializedErrorClass = getClass("scala.UninitializedFieldError") // annotations lazy val AnnotationClass = getClass("scala.Annotation") diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index d22d5c1602..6de47e6c3d 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -31,9 +31,8 @@ abstract class ICodeReader extends ClassfileParser { var staticCode: IClass = null // the ICode class static members var method: IMethod = _ // the current IMethod - val OBJECT: TypeKind = REFERENCE(definitions.ObjectClass) - val nothingName = newTermName(SCALA_NOTHING) - val nullName = newTermName(SCALA_NULL) + val nothingName = newTermName(ClassfileConstants.SCALA_NOTHING) + val nullName = newTermName(ClassfileConstants.SCALA_NULL) var isScalaModule = false /** Read back bytecode for the given class symbol. It returns @@ -288,7 +287,7 @@ abstract class ICodeReader extends ClassfileParser { if (local == 0 && !method.isStatic) code.emit(THIS(method.symbol.owner)); else - code.emit(LOAD_LOCAL(code.getLocal(local, OBJECT))); + code.emit(LOAD_LOCAL(code.getLocal(local, ObjectReference))); case JVM.iload_0 => code.emit(LOAD_LOCAL(code.getLocal(0, INT))) case JVM.iload_1 => code.emit(LOAD_LOCAL(code.getLocal(1, INT))) @@ -310,16 +309,16 @@ abstract class ICodeReader extends ClassfileParser { if (!method.isStatic) code.emit(THIS(method.symbol.owner)); else - code.emit(LOAD_LOCAL(code.getLocal(0, OBJECT))); - case JVM.aload_1 => code.emit(LOAD_LOCAL(code.getLocal(1, OBJECT))) - case JVM.aload_2 => code.emit(LOAD_LOCAL(code.getLocal(2, OBJECT))) - case JVM.aload_3 => code.emit(LOAD_LOCAL(code.getLocal(3, OBJECT))) + code.emit(LOAD_LOCAL(code.getLocal(0, ObjectReference))); + case JVM.aload_1 => code.emit(LOAD_LOCAL(code.getLocal(1, ObjectReference))) + case JVM.aload_2 => code.emit(LOAD_LOCAL(code.getLocal(2, ObjectReference))) + case JVM.aload_3 => code.emit(LOAD_LOCAL(code.getLocal(3, ObjectReference))) case JVM.iaload => code.emit(LOAD_ARRAY_ITEM(INT)) case JVM.laload => code.emit(LOAD_ARRAY_ITEM(LONG)) case JVM.faload => code.emit(LOAD_ARRAY_ITEM(FLOAT)) case JVM.daload => code.emit(LOAD_ARRAY_ITEM(DOUBLE)) - case JVM.aaload => code.emit(LOAD_ARRAY_ITEM(OBJECT)) + case JVM.aaload => code.emit(LOAD_ARRAY_ITEM(ObjectReference)) case JVM.baload => code.emit(LOAD_ARRAY_ITEM(BYTE)) case JVM.caload => code.emit(LOAD_ARRAY_ITEM(CHAR)) case JVM.saload => code.emit(LOAD_ARRAY_ITEM(SHORT)) @@ -328,7 +327,7 @@ abstract class ICodeReader extends ClassfileParser { case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, LONG))); size += 1 case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, FLOAT))); size += 1 case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, DOUBLE))); size += 1 - case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, OBJECT))); size += 1 + case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, ObjectReference))); size += 1 case JVM.istore_0 => code.emit(STORE_LOCAL(code.getLocal(0, INT))) case JVM.istore_1 => code.emit(STORE_LOCAL(code.getLocal(1, INT))) case JVM.istore_2 => code.emit(STORE_LOCAL(code.getLocal(2, INT))) @@ -347,24 +346,24 @@ abstract class ICodeReader extends ClassfileParser { case JVM.dstore_3 => code.emit(STORE_LOCAL(code.getLocal(3, DOUBLE))) case JVM.astore_0 => if (method.isStatic) - code.emit(STORE_LOCAL(code.getLocal(0, OBJECT))) + code.emit(STORE_LOCAL(code.getLocal(0, ObjectReference))) else - code.emit(STORE_THIS(OBJECT)) - case JVM.astore_1 => code.emit(STORE_LOCAL(code.getLocal(1, OBJECT))) - case JVM.astore_2 => code.emit(STORE_LOCAL(code.getLocal(2, OBJECT))) - case JVM.astore_3 => code.emit(STORE_LOCAL(code.getLocal(3, OBJECT))) + code.emit(STORE_THIS(ObjectReference)) + case JVM.astore_1 => code.emit(STORE_LOCAL(code.getLocal(1, ObjectReference))) + case JVM.astore_2 => code.emit(STORE_LOCAL(code.getLocal(2, ObjectReference))) + case JVM.astore_3 => code.emit(STORE_LOCAL(code.getLocal(3, ObjectReference))) case JVM.iastore => code.emit(STORE_ARRAY_ITEM(INT)) case JVM.lastore => code.emit(STORE_ARRAY_ITEM(LONG)) case JVM.fastore => code.emit(STORE_ARRAY_ITEM(FLOAT)) case JVM.dastore => code.emit(STORE_ARRAY_ITEM(DOUBLE)) - case JVM.aastore => code.emit(STORE_ARRAY_ITEM(OBJECT)) + case JVM.aastore => code.emit(STORE_ARRAY_ITEM(ObjectReference)) case JVM.bastore => code.emit(STORE_ARRAY_ITEM(BYTE)) case JVM.castore => code.emit(STORE_ARRAY_ITEM(CHAR)) case JVM.sastore => code.emit(STORE_ARRAY_ITEM(SHORT)) case JVM.pop => code.emit(DROP(INT)) // any 1-word type would do case JVM.pop2 => code.emit(DROP(LONG)) // any 2-word type would do - case JVM.dup => code.emit(DUP(OBJECT)) // TODO: Is the kind inside DUP ever needed? + case JVM.dup => code.emit(DUP(ObjectReference)) // TODO: Is the kind inside DUP ever needed? case JVM.dup_x1 => code.emit(DUP_X1) // Predef.error("Unsupported JVM bytecode: dup_x1") case JVM.dup_x2 => code.emit(DUP_X2) // Predef.error("Unsupported JVM bytecode: dup_x2") case JVM.dup2 => code.emit(DUP(LONG)) // TODO: Is the kind inside DUP ever needed? @@ -453,8 +452,8 @@ abstract class ICodeReader extends ClassfileParser { case JVM.if_icmpge => code.emit(LCJUMP(parseJumpTarget, pc + size, GE, INT)) case JVM.if_icmpgt => code.emit(LCJUMP(parseJumpTarget, pc + size, GT, INT)) case JVM.if_icmple => code.emit(LCJUMP(parseJumpTarget, pc + size, LE, INT)) - case JVM.if_acmpeq => code.emit(LCJUMP(parseJumpTarget, pc + size, EQ, OBJECT)) - case JVM.if_acmpne => code.emit(LCJUMP(parseJumpTarget, pc + size, NE, OBJECT)) + case JVM.if_acmpeq => code.emit(LCJUMP(parseJumpTarget, pc + size, EQ, ObjectReference)) + case JVM.if_acmpne => code.emit(LCJUMP(parseJumpTarget, pc + size, NE, ObjectReference)) case JVM.goto => emit(LJUMP(parseJumpTarget)) case JVM.jsr => Predef.error("Cannot handle jsr/ret") @@ -501,7 +500,7 @@ abstract class ICodeReader extends ClassfileParser { case JVM.lreturn => code.emit(RETURN(LONG)) case JVM.freturn => code.emit(RETURN(FLOAT)) case JVM.dreturn => code.emit(RETURN(DOUBLE)) - case JVM.areturn => code.emit(RETURN(OBJECT)) + case JVM.areturn => code.emit(RETURN(ObjectReference)) case JVM.return_ => code.emit(RETURN(UNIT)) case JVM.getstatic => @@ -562,8 +561,8 @@ abstract class ICodeReader extends ClassfileParser { val tpe = pool.getClassOrArrayType(in.nextChar); size += 2 code.emit(CREATE_ARRAY(toTypeKind(tpe), 1)) - case JVM.arraylength => code.emit(CALL_PRIMITIVE(ArrayLength(OBJECT))); // the kind does not matter - case JVM.athrow => code.emit(THROW()); + case JVM.arraylength => code.emit(CALL_PRIMITIVE(ArrayLength(ObjectReference))); // the kind does not matter + case JVM.athrow => code.emit(THROW(definitions.ThrowableClass)) case JVM.checkcast => code.emit(CHECK_CAST(toTypeKind(pool.getClassOrArrayType(in.nextChar)))); size += 2 case JVM.instanceof => @@ -577,12 +576,12 @@ abstract class ICodeReader extends ClassfileParser { case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2 case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2 case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2 - case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, OBJECT))); size += 2 + case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2 case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, INT))); size += 2 case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2 case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2 case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2 - case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, OBJECT))); size += 2 + case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2 case JVM.ret => Predef.error("Cannot handle jsr/ret") case JVM.iinc => size += 4 @@ -600,8 +599,8 @@ abstract class ICodeReader extends ClassfileParser { // assert(dim == 1, "Cannot handle multidimensional arrays yet.") code.emit(CREATE_ARRAY(tpe, dim)) - case JVM.ifnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, EQ, OBJECT)) - case JVM.ifnonnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, NE, OBJECT)) + case JVM.ifnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, EQ, ObjectReference)) + case JVM.ifnonnull => code.emit(LCZJUMP(parseJumpTarget, pc + size, NE, ObjectReference)) case JVM.goto_w => code.emit(LJUMP(parseJumpTargetW)) case JVM.jsr_w => Predef.error("Cannot handle jsr/ret") @@ -739,7 +738,7 @@ abstract class ICodeReader extends ClassfileParser { bb.emit(instr) bb.close - case THROW() => + case THROW(clasz) => bb.emit(instr) bb.close |