From e78896f0ab7240e9bdcd98c51cbf6bc0ea277950 Mon Sep 17 00:00:00 2001 From: James Iry Date: Fri, 8 Mar 2013 06:33:12 -0800 Subject: Read version 51 (JDK 7) class files. This commit makes the ClassFileReader/ICodeReader parse class files from JDK 7 (class file version 51). It does that by skipping over the method handle related entries in the constant pool and by doing some dummy processing on invoke dynamic instructions. The inliner is updated to not try to inline a method with an invoke dynamic instruction. A place holder INVOKE_DYNAMIC instruction is added to ICode but it is designed to create an error if there's ever any attempt to analyze it. Because the inliner is the only phase that ever tries to analyze ICode instructions not generated from Scala source and because Scala source will never emit an INVOKE_DYNAMIC, the place holder INVOKE_DYNAMIC should never cause any errors. A test is included that generates a class file with INVOKE_DYNAMIC and then compiles Scala code that depends on it. --- .../scala/tools/nsc/backend/icode/Members.scala | 1 + .../scala/tools/nsc/backend/icode/Opcodes.scala | 19 +++++++++++++++++++ .../scala/tools/nsc/backend/opt/Inliners.scala | 1 + .../tools/nsc/symtab/classfile/ClassfileParser.scala | 7 +++++-- .../tools/nsc/symtab/classfile/ICodeReader.scala | 8 ++++++++ .../scala/reflect/internal/ClassfileConstants.scala | 5 ++++- 6 files changed, 38 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 7ba212f42e..b74770f051 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -171,6 +171,7 @@ trait Members { var returnType: TypeKind = _ var recursive: Boolean = false var bytecodeHasEHs = false // set by ICodeReader only, used by Inliner to prevent inlining (SI-6188) + var bytecodeHasInvokeDynamic = false // set by ICodeReader only, used by Inliner to prevent inlining until we have proper invoke dynamic support /** local variables and method parameters */ var locals: List[Local] = Nil diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 8c9a72638d..a3a0edb35d 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -409,6 +409,25 @@ trait Opcodes { self: ICodes => override def category = mthdsCat } + + /** + * A place holder entry that allows us to parse class files with invoke dynamic + * instructions. Because the compiler doesn't yet really understand the + * behavior of invokeDynamic, this op acts as a poison pill. Any attempt to analyze + * this instruction will cause a failure. The only optimization that + * should ever look at non-Scala generated icode is the inliner, and it + * has been modified to not examine any method with invokeDynamic + * instructions. So if this poison pill ever causes problems then + * there's been a serious misunderstanding + */ + // TODO do the real thing + case class INVOKE_DYNAMIC(poolEntry: Char) extends Instruction { + private def error = sys.error("INVOKE_DYNAMIC is not fully implemented and should not be analyzed") + override def consumed = error + override def produced = error + override def producedTypes = error + override def category = error + } case class BOX(boxType: TypeKind) extends Instruction { assert(boxType.isValueType && (boxType ne UNIT)) // documentation diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 521b6cc132..498db78636 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -961,6 +961,7 @@ abstract class Inliners extends SubComponent { if(isInlineForbidden) { rs ::= "is annotated @noinline" } if(inc.isSynchronized) { rs ::= "is synchronized method" } if(inc.m.bytecodeHasEHs) { rs ::= "bytecode contains exception handlers / finally clause" } // SI-6188 + if(inc.m.bytecodeHasInvokeDynamic) { rs ::= "bytecode contains invoke dynamic" } if(rs.isEmpty) null else rs.mkString("", ", and ", "") } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 1f42fa8aab..29cac86c16 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -137,10 +137,13 @@ abstract class ClassfileParser { (in.nextByte.toInt: @switch) match { case CONSTANT_UTF8 | CONSTANT_UNICODE => in.skip(in.nextChar) - case CONSTANT_CLASS | CONSTANT_STRING => + case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE=> in.skip(2) + case CONSTANT_METHODHANDLE => + in.skip(3) case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF - | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => + | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT + | CONSTANT_INVOKEDYNAMIC => in.skip(4) case CONSTANT_LONG | CONSTANT_DOUBLE => in.skip(8) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 13c0d8993a..d38907d182 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -506,6 +506,12 @@ abstract class ICodeReader extends ClassfileParser { code.emit(UNBOX(toTypeKind(m.info.resultType))) else code.emit(CALL_METHOD(m, Static(false))) + case JVM.invokedynamic => + // TODO, this is just a place holder. A real implementation must parse the class constant entry + containsInvokeDynamic = true + val poolEntry = in.nextChar + in.skip(2) + code.emit(INVOKE_DYNAMIC(poolEntry)) case JVM.new_ => code.emit(NEW(REFERENCE(pool.getClassSymbol(in.nextChar)))) @@ -644,6 +650,7 @@ abstract class ICodeReader extends ClassfileParser { var containsDUPX = false var containsNEW = false var containsEHs = false + var containsInvokeDynamic = false def emit(i: Instruction) { instrs += ((pc, i)) @@ -662,6 +669,7 @@ abstract class ICodeReader extends ClassfileParser { val code = new Code(method) method.setCode(code) method.bytecodeHasEHs = containsEHs + method.bytecodeHasInvokeDynamic = containsInvokeDynamic var bb = code.startBlock def makeBasicBlocks: mutable.Map[Int, BasicBlock] = diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala index 7ccb661426..c198271fb1 100644 --- a/src/reflect/scala/reflect/internal/ClassfileConstants.scala +++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala @@ -72,6 +72,9 @@ object ClassfileConstants { final val CONSTANT_METHODREF = 10 final val CONSTANT_INTFMETHODREF = 11 final val CONSTANT_NAMEANDTYPE = 12 + final val CONSTANT_METHODHANDLE = 15 + final val CONSTANT_METHODTYPE = 16 + final val CONSTANT_INVOKEDYNAMIC = 18 // tags describing the type of a literal in attribute values final val BYTE_TAG = 'B' @@ -306,7 +309,7 @@ object ClassfileConstants { final val invokespecial = 0xb7 final val invokestatic = 0xb8 final val invokeinterface = 0xb9 - final val xxxunusedxxxx = 0xba + final val invokedynamic = 0xba final val new_ = 0xbb final val newarray = 0xbc -- cgit v1.2.3