summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala773
1 files changed, 372 insertions, 401 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 7c46d648fe..e50b73dcc3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -3,17 +3,16 @@
* @author Martin Odersky
*/
-package scala.tools.nsc
+package scala
+package tools.nsc
package backend.jvm
-import java.nio.ByteBuffer
import scala.collection.{ mutable, immutable }
import scala.reflect.internal.pickling.{ PickleFormat, PickleBuffer }
import scala.tools.nsc.symtab._
-import scala.tools.nsc.io.AbstractFile
-
import scala.tools.asm
import asm.Label
+import scala.annotation.tailrec
/**
* @author Iulian Dragos (version 1.0, FJBG-based implementation)
@@ -27,11 +26,25 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
import icodes.opcodes._
import definitions._
+ // Strangely I can't find this in the asm code
+ // 255, but reserving 1 for "this"
+ final val MaximumJvmParameters = 254
+
val phaseName = "jvm"
/** Create a new phase */
override def newPhase(p: Phase): Phase = new AsmPhase(p)
+ /** From the reference documentation of the Android SDK:
+ * The `Parcelable` interface identifies classes whose instances can be written to and restored from a `Parcel`.
+ * Classes implementing the `Parcelable` interface must also have a static field called `CREATOR`,
+ * which is an object implementing the `Parcelable.Creator` interface.
+ */
+ private val androidFieldName = newTermName("CREATOR")
+
+ private lazy val AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable")
+ private lazy val AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator")
+
/** JVM code generation phase
*/
class AsmPhase(prev: Phase) extends ICodePhase(prev) {
@@ -39,7 +52,25 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
override def erasedTypes = true
def apply(cls: IClass) = sys.error("no implementation")
- val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo")
+ // An AsmPhase starts and ends within a Run, thus the caches in question will get populated and cleared within a Run, too), SI-7422
+ javaNameCache.clear()
+ javaNameCache ++= List(
+ NothingClass -> binarynme.RuntimeNothing,
+ RuntimeNothingClass -> binarynme.RuntimeNothing,
+ NullClass -> binarynme.RuntimeNull,
+ RuntimeNullClass -> binarynme.RuntimeNull
+ )
+
+ // unlike javaNameCache, reverseJavaName contains entries only for class symbols and their internal names.
+ reverseJavaName.clear()
+ reverseJavaName ++= List(
+ binarynme.RuntimeNothing.toString() -> RuntimeNothingClass, // RuntimeNothingClass is the bytecode-level return type of Scala methods with Nothing return-type.
+ binarynme.RuntimeNull.toString() -> RuntimeNullClass
+ )
+
+ // Lazy val; can't have eager vals in Phase constructors which may
+ // cause cycles before Global has finished initialization.
+ lazy val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo")
private def initBytecodeWriter(entryPoints: List[IClass]): BytecodeWriter = {
settings.outputDirs.getSingleOutput match {
@@ -61,29 +92,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
new DirectToJarfileWriter(f.file)
- case _ =>
- if (settings.Ygenjavap.isDefault) {
- if(settings.Ydumpclasses.isDefault)
- new ClassBytecodeWriter { }
- else
- new ClassBytecodeWriter with DumpBytecodeWriter { }
- }
- else new ClassBytecodeWriter with JavapBytecodeWriter { }
-
- // TODO A ScalapBytecodeWriter could take asm.util.Textifier as starting point.
- // Three areas where javap ouput is less than ideal (e.g. when comparing versions of the same classfile) are:
- // (a) unreadable pickle;
- // (b) two constant pools, while having identical contents, are displayed differently due to physical layout.
- // (c) stack maps (classfile version 50 and up) are displayed in encoded form by javap, their expansion makes more sense instead.
+ case _ => factoryNonJarBytecodeWriter()
}
}
override def run() {
- if (settings.debug.value)
+ if (settings.debug)
inform("[running phase " + name + " on icode]")
- if (settings.Xdce.value)
+ if (settings.Xdce)
for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) {
log(s"Optimizer eliminated ${sym.fullNameString}")
icodes.classes -= sym
@@ -99,41 +117,41 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
"Such classes will overwrite one another on case-insensitive filesystems.")
}
- debuglog("Created new bytecode generator for " + classes.size + " classes.")
+ debuglog(s"Created new bytecode generator for ${classes.size} classes.")
val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint)
- val plainCodeGen = new JPlainBuilder(bytecodeWriter)
- val mirrorCodeGen = new JMirrorBuilder(bytecodeWriter)
- val beanInfoCodeGen = new JBeanInfoBuilder(bytecodeWriter)
-
- while(!sortedClasses.isEmpty) {
- val c = sortedClasses.head
+ val needsOutfile = bytecodeWriter.isInstanceOf[ClassBytecodeWriter]
+ val plainCodeGen = new JPlainBuilder( bytecodeWriter, needsOutfile)
+ val mirrorCodeGen = new JMirrorBuilder( bytecodeWriter, needsOutfile)
+ val beanInfoCodeGen = new JBeanInfoBuilder(bytecodeWriter, needsOutfile)
+ def emitFor(c: IClass) {
if (isStaticModule(c.symbol) && isTopLevelModule(c.symbol)) {
- if (c.symbol.companionClass == NoSymbol) {
- mirrorCodeGen.genMirrorClass(c.symbol, c.cunit)
- } else {
- log("No mirror class for module with linked class: " + c.symbol.fullName)
- }
+ if (c.symbol.companionClass == NoSymbol)
+ mirrorCodeGen genMirrorClass (c.symbol, c.cunit)
+ else
+ log(s"No mirror class for module with linked class: ${c.symbol.fullName}")
}
+ plainCodeGen genClass c
+ if (c.symbol hasAnnotation BeanInfoAttr) beanInfoCodeGen genBeanInfoClass c
+ }
- plainCodeGen.genClass(c)
-
- if (c.symbol hasAnnotation BeanInfoAttr) {
- beanInfoCodeGen.genBeanInfoClass(c)
+ while (!sortedClasses.isEmpty) {
+ val c = sortedClasses.head
+ try emitFor(c)
+ catch {
+ case e: FileConflictException =>
+ c.cunit.error(c.symbol.pos, s"error writing ${c.symbol}: ${e.getMessage}")
}
-
sortedClasses = sortedClasses.tail
classes -= c.symbol // GC opportunity
}
bytecodeWriter.close()
- classes.clear()
- reverseJavaName.clear()
/* don't javaNameCache.clear() because that causes the following tests to fail:
* test/files/run/macro-repl-dontexpand.scala
* test/files/jvm/interpreter.scala
- * TODO but why? what use could javaNameCache possibly see once GenJVM is over?
+ * TODO but why? what use could javaNameCache possibly see once GenASM is over?
*/
/* TODO After emitting all class files (e.g., in a separate compiler phase) ASM can perform bytecode verification:
@@ -152,19 +170,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
var pickledBytes = 0 // statistics
- // Don't put this in per run caches. Contains entries for classes as well as members.
- val javaNameCache = new mutable.WeakHashMap[Symbol, Name]() ++= List(
- NothingClass -> binarynme.RuntimeNothing,
- RuntimeNothingClass -> binarynme.RuntimeNothing,
- NullClass -> binarynme.RuntimeNull,
- RuntimeNullClass -> binarynme.RuntimeNull
- )
+ val javaNameCache = perRunCaches.newMap[Symbol, Name]()
// unlike javaNameCache, reverseJavaName contains entries only for class symbols and their internal names.
- val reverseJavaName = mutable.Map.empty[String, Symbol] ++= List(
- binarynme.RuntimeNothing.toString() -> RuntimeNothingClass, // RuntimeNothingClass is the bytecode-level return type of Scala methods with Nothing return-type.
- binarynme.RuntimeNull.toString() -> RuntimeNullClass
- )
+ val reverseJavaName = perRunCaches.newMap[String, Symbol]()
private def mkFlags(args: Int*) = args.foldLeft(0)(_ | _)
private def hasPublicBitSet(flags: Int) = (flags & asm.Opcodes.ACC_PUBLIC) != 0
@@ -248,7 +257,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
def isTopLevelModule(sym: Symbol): Boolean =
- afterPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass }
+ exitingPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass }
def isStaticModule(sym: Symbol): Boolean = {
sym.isModuleClass && !sym.isImplClass && !sym.isLifted
@@ -283,7 +292,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
def inameToSymbol(iname: String): Symbol = {
val name = global.newTypeName(iname)
val res0 =
- if (nme.isModuleName(name)) rootMirror.getModule(nme.stripModuleSuffix(name))
+ if (nme.isModuleName(name)) rootMirror.getModule(name.dropModule)
else rootMirror.getClassByName(name.replace('/', '.')) // TODO fails for inner classes (but this hasn't been tested).
assert(res0 != NoSymbol)
val res = jsymbol(res0)
@@ -368,7 +377,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
private val classfileVersion: Int = settings.target.value match {
case "jvm-1.5" => asm.Opcodes.V1_5
- case "jvm-1.5-asm" => asm.Opcodes.V1_5
case "jvm-1.6" => asm.Opcodes.V1_6
case "jvm-1.7" => asm.Opcodes.V1_7
}
@@ -396,9 +404,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
/** basic functionality for class file building */
- abstract class JBuilder(bytecodeWriter: BytecodeWriter) {
+ abstract class JBuilder(bytecodeWriter: BytecodeWriter, needsOutfile: Boolean) {
- val EMPTY_JTYPE_ARRAY = Array.empty[asm.Type]
val EMPTY_STRING_ARRAY = Array.empty[String]
val mdesc_arglessvoid = "()V"
@@ -443,19 +450,22 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
def createJAttribute(name: String, b: Array[Byte], offset: Int, len: Int): asm.Attribute = {
- val dest = new Array[Byte](len);
- System.arraycopy(b, offset, dest, 0, len);
+ val dest = new Array[Byte](len)
+ System.arraycopy(b, offset, dest, 0, len)
new asm.CustomAttr(name, dest)
}
// -----------------------------------------------------------------------------------------
- // utitilies useful when emitting plain, mirror, and beaninfo classes.
+ // utilities useful when emitting plain, mirror, and beaninfo classes.
// -----------------------------------------------------------------------------------------
def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) {
try {
val arr = jclass.toByteArray()
- bytecodeWriter.writeClass(label, jclassName, arr, sym)
+ val outF: scala.tools.nsc.io.AbstractFile = {
+ if(needsOutfile) getFile(sym, jclassName, ".class") else null
+ }
+ bytecodeWriter.writeClass(label, jclassName, arr, outF)
} catch {
case e: java.lang.RuntimeException if(e.getMessage() == "Class file too large!") =>
// TODO check where ASM throws the equivalent of CodeSizeTooBigException
@@ -466,7 +476,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
/** Specialized array conversion to prevent calling
* java.lang.reflect.Array.newInstance via TraversableOnce.toArray
*/
- def mkArray(xs: Traversable[asm.Type]): Array[asm.Type] = { val a = new Array[asm.Type](xs.size); xs.copyToArray(a); a }
def mkArray(xs: Traversable[String]): Array[String] = { val a = new Array[String](xs.size); xs.copyToArray(a); a }
// -----------------------------------------------------------------------------------------
@@ -509,14 +518,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
*/
def javaName(sym: Symbol): String = {
- /**
+ /*
* Checks if given symbol corresponds to inner class/object and add it to innerClassBuffer
*
* Note: This method is called recursively thus making sure that we add complete chain
* of inner class all until root class.
*/
def collectInnerClass(s: Symbol): Unit = {
- // TODO: some beforeFlatten { ... } which accounts for
+ // TODO: some enteringFlatten { ... } which accounts for
// being nested in parameterized classes (if we're going to selectively flatten.)
val x = innerClassSymbolFor(s)
if(x ne NoSymbol) {
@@ -531,7 +540,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
collectInnerClass(sym)
- var hasInternalName = (sym.isClass || (sym.isModule && !sym.isMethod))
+ val hasInternalName = sym.isClass || sym.isModuleNotMethod
val cachedJN = javaNameCache.getOrElseUpdate(sym, {
if (hasInternalName) { sym.javaBinaryName }
else { sym.javaSimpleName }
@@ -541,12 +550,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
val internalName = cachedJN.toString()
val trackedSym = jsymbol(sym)
reverseJavaName.get(internalName) match {
- case None =>
+ case Some(oldsym) if oldsym.exists && trackedSym.exists =>
+ assert(
+ // In contrast, neither NothingClass nor NullClass show up bytecode-level.
+ (oldsym == trackedSym) || (oldsym == RuntimeNothingClass) || (oldsym == RuntimeNullClass) || (oldsym.isModuleClass && (oldsym.sourceModule == trackedSym.sourceModule)),
+ s"""|Different class symbols have the same bytecode-level internal name:
+ | name: $internalName
+ | oldsym: ${oldsym.fullNameString}
+ | tracked: ${trackedSym.fullNameString}
+ """.stripMargin
+ )
+ case _ =>
reverseJavaName.put(internalName, trackedSym)
- case Some(oldsym) =>
- assert((oldsym == trackedSym) || (oldsym == RuntimeNothingClass) || (oldsym == RuntimeNullClass) ||
- (oldsym.isModuleClass && (oldsym.sourceModule == trackedSym.sourceModule)), // In contrast, neither NothingClass nor NullClass show up bytecode-level.
- "how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name: " + internalName)
}
}
@@ -588,7 +603,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
def javaType(s: Symbol): asm.Type = {
if (s.isMethod) {
- val resT: asm.Type = if (s.isClassConstructor) asm.Type.VOID_TYPE else javaType(s.tpe.resultType);
+ val resT: asm.Type = if (s.isClassConstructor) asm.Type.VOID_TYPE else javaType(s.tpe.resultType)
asm.Type.getMethodType( resT, (s.tpe.paramTypes map javaType): _*)
} else { javaType(s.tpe) }
}
@@ -598,9 +613,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) }
def addInnerClasses(csym: Symbol, jclass: asm.ClassVisitor) {
- /** The outer name for this inner class. Note that it returns null
- * when the inner class should not get an index in the constant pool.
- * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS.
+ /* The outer name for this inner class. Note that it returns null
+ * when the inner class should not get an index in the constant pool.
+ * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS.
*/
def outerName(innerSym: Symbol): String = {
if (innerSym.originalEnclosingMethod != NoSymbol)
@@ -619,7 +634,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
innerSym.rawname + innerSym.moduleSuffix
// add inner classes which might not have been referenced yet
- afterErasure {
+ exitingErasure {
for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass)
innerClassBuffer += m
}
@@ -681,7 +696,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
/** functionality for building plain and mirror classes */
- abstract class JCommonBuilder(bytecodeWriter: BytecodeWriter) extends JBuilder(bytecodeWriter) {
+ abstract class JCommonBuilder(bytecodeWriter: BytecodeWriter, needsOutfile: Boolean) extends JBuilder(bytecodeWriter, needsOutfile) {
def debugLevel = settings.debuginfo.indexOfChoice
@@ -793,7 +808,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// without it. This is particularly bad because the availability of
// generic information could disappear as a consequence of a seemingly
// unrelated change.
- settings.Ynogenericsig.value
+ settings.Ynogenericsig
|| sym.isArtifact
|| sym.isLiftedMethod
|| sym.isBridge
@@ -810,7 +825,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if (!needsGenericSignature(sym)) { return null }
- val memberTpe = beforeErasure(owner.thisType.memberInfo(sym))
+ val memberTpe = enteringErasure(owner.thisType.memberInfo(sym))
val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe)
if (jsOpt.isEmpty) { return null }
@@ -823,14 +838,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
catch { case _: Throwable => false }
}
- if (settings.Xverify.value) {
+ if (settings.Xverify) {
// Run the signature parser to catch bogus signatures.
val isValidSignature = wrap {
// Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser)
- import scala.tools.asm.util.SignatureChecker
- if (sym.isMethod) { SignatureChecker checkMethodSignature sig } // requires asm-util.jar
- else if (sym.isTerm) { SignatureChecker checkFieldSignature sig }
- else { SignatureChecker checkClassSignature sig }
+ import scala.tools.asm.util.CheckClassAdapter
+ if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar
+ else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig }
+ else { CheckClassAdapter checkClassSignature sig }
}
if(!isValidSignature) {
@@ -844,7 +859,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
if ((settings.check containsName phaseName)) {
- val normalizedTpe = beforeErasure(erasure.prepareSigMap(memberTpe))
+ val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe))
val bytecodeTpe = owner.thisType.memberInfo(sym)
if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) {
getCurrentCUnit().warning(sym.pos,
@@ -863,9 +878,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = {
- val ca = new Array[Char](bytes.size)
+ val ca = new Array[Char](bytes.length)
var idx = 0
- while(idx < bytes.size) {
+ while(idx < bytes.length) {
val b: Byte = bytes(idx)
assert((b & ~0x7f) == 0)
ca(idx) = b.asInstanceOf[Char]
@@ -882,7 +897,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
var prevOffset = 0
var offset = 0
var encLength = 0
- while(offset < bSeven.size) {
+ while(offset < bSeven.length) {
val deltaEncLength = (if(bSeven(offset) == 0) 2 else 1)
val newEncLength = encLength.toLong + deltaEncLength
if(newEncLength >= 65535) {
@@ -1034,9 +1049,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
val paramJavaTypes: List[asm.Type] = methodInfo.paramTypes map javaType
// val paramNames = 0 until paramJavaTypes.length map ("x_" + _)
- /** Forwarders must not be marked final,
- * as the JVM will not allow redefinition of a final static method,
- * and we don't know what classes might be subclassing the companion class. See SI-4827.
+ /* Forwarders must not be marked final,
+ * as the JVM will not allow redefinition of a final static method,
+ * and we don't know what classes might be subclassing the companion class. See SI-4827.
*/
// TODO: evaluate the other flags we might be dropping on the floor here.
// TODO: ACC_SYNTHETIC ?
@@ -1099,7 +1114,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
debuglog("Dumping mirror class for object: " + moduleClass)
val linkedClass = moduleClass.companionClass
- val linkedModule = linkedClass.companionSymbol
lazy val conflictingNames: Set[Name] = {
(linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name }).toSet
}
@@ -1125,16 +1139,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
trait JAndroidBuilder {
self: JPlainBuilder =>
- /** From the reference documentation of the Android SDK:
- * The `Parcelable` interface identifies classes whose instances can be written to and restored from a `Parcel`.
- * Classes implementing the `Parcelable` interface must also have a static field called `CREATOR`,
- * which is an object implementing the `Parcelable.Creator` interface.
- */
- private val androidFieldName = newTermName("CREATOR")
-
- private lazy val AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable")
- private lazy val AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator")
-
def isAndroidParcelableClass(sym: Symbol) =
(AndroidParcelableInterface != NoSymbol) &&
(sym.parentSymbols contains AndroidParcelableInterface)
@@ -1142,13 +1146,13 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
/* Typestate: should be called before emitting fields (because it adds an IField to the current IClass). */
def addCreatorCode(block: BasicBlock) {
val fieldSymbol = (
- clasz.symbol.newValue(newTermName(androidFieldName), NoPosition, Flags.STATIC | Flags.FINAL)
+ clasz.symbol.newValue(androidFieldName, NoPosition, Flags.STATIC | Flags.FINAL)
setInfo AndroidCreatorClass.tpe
)
val methodSymbol = definitions.getMember(clasz.symbol.companionModule, androidFieldName)
clasz addField new IField(fieldSymbol)
- block emit CALL_METHOD(methodSymbol, Static(false))
- block emit STORE_FIELD(fieldSymbol, true)
+ block emit CALL_METHOD(methodSymbol, Static(onInstance = false))
+ block emit STORE_FIELD(fieldSymbol, isStatic = true)
}
def legacyAddCreatorCode(clinit: asm.MethodVisitor) {
@@ -1157,7 +1161,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
jclass.visitField(
PublicStaticFinal,
- androidFieldName,
+ androidFieldName.toString,
tdesc_creator,
null, // no java-generic-signature
null // no initial value
@@ -1177,7 +1181,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
clinit.visitMethodInsn(
asm.Opcodes.INVOKEVIRTUAL,
moduleName,
- androidFieldName,
+ androidFieldName.toString,
asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*)
)
@@ -1185,7 +1189,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
clinit.visitFieldInsn(
asm.Opcodes.PUTSTATIC,
thisName,
- androidFieldName,
+ androidFieldName.toString,
tdesc_creator
)
}
@@ -1242,8 +1246,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case class BlockInteval(start: BasicBlock, end: BasicBlock)
/** builder of plain classes */
- class JPlainBuilder(bytecodeWriter: BytecodeWriter)
- extends JCommonBuilder(bytecodeWriter)
+ class JPlainBuilder(bytecodeWriter: BytecodeWriter, needsOutfile: Boolean)
+ extends JCommonBuilder(bytecodeWriter, needsOutfile)
with JAndroidBuilder {
val MIN_SWITCH_DENSITY = 0.7
@@ -1267,14 +1271,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// Additional interface parents based on annotations and other cues
def newParentForAttr(attr: Symbol): Option[Symbol] = attr match {
- case SerializableAttr => Some(SerializableClass)
- case CloneableAttr => Some(CloneableClass)
case RemoteAttr => Some(RemoteInterfaceClass)
case _ => None
}
- /** Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents.
- * This is important on Android because there is otherwise an interface explosion.
+ /* Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents.
+ * This is important on Android because there is otherwise an interface explosion.
*/
def minimizeInterfaces(lstIfaces: List[Symbol]): List[Symbol] = {
var rest = lstIfaces
@@ -1292,7 +1294,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
val ps = c.symbol.info.parents
- val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses;
+ val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses
val superInterfaces = (superInterfaces0 ++ c.symbol.annotations.flatMap(ann => newParentForAttr(ann.symbol))).distinct
if(superInterfaces.isEmpty) EMPTY_STRING_ARRAY
@@ -1317,7 +1319,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
thisName = javaName(c.symbol) // the internal name of the class being emitted
val ps = c.symbol.info.parents
- val superClass: String = if(ps.isEmpty) JAVA_LANG_OBJECT.getInternalName else javaName(ps.head.typeSymbol);
+ val superClass: String = if(ps.isEmpty) JAVA_LANG_OBJECT.getInternalName else javaName(ps.head.typeSymbol)
val ifaces = getSuperInterfaces(c)
@@ -1364,14 +1366,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
for (constructor <- c.lookupStaticCtor) {
addStaticInit(Some(constructor))
}
- val skipStaticForwarders = (c.symbol.isInterface || settings.noForwarders.value)
+ val skipStaticForwarders = (c.symbol.isInterface || settings.noForwarders)
if (!skipStaticForwarders) {
val lmoc = c.symbol.companionModule
// add static forwarders if there are no name conflicts; see bugs #363 and #1735
if (lmoc != NoSymbol) {
// it must be a top level class (name contains no $s)
val isCandidateForForwarders = {
- afterPickler { !(lmoc.name.toString contains '$') && lmoc.hasModuleFlag && !lmoc.isImplClass && !lmoc.isNestedClass }
+ exitingPickler { !(lmoc.name.toString contains '$') && lmoc.hasModuleFlag && !lmoc.isImplClass && !lmoc.isNestedClass }
}
if (isCandidateForForwarders) {
log("Adding static forwarders from '%s' to implementations in '%s'".format(c.symbol, lmoc))
@@ -1400,7 +1402,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
addInnerClasses(clasz.symbol, jclass)
jclass.visitEnd()
writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol)
-
}
/**
@@ -1432,7 +1433,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
assert(enclClass.isClass, enclClass)
val sym = enclClass.primaryConstructor
if (sym == NoSymbol) {
- log("Ran out of room looking for an enclosing method for %s: no constructor here.".format(enclClass, clazz))
+ log("Ran out of room looking for an enclosing method for %s: no constructor here.".format(enclClass))
} else {
debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass))
res = EnclMethodEntry(javaName(enclClass), javaName(sym), javaType(sym))
@@ -1483,6 +1484,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if (m.symbol.isStaticConstructor || definitions.isGetClass(m.symbol)) return
+ if (m.params.size > MaximumJvmParameters) {
+ getCurrentCUnit().error(m.symbol.pos, s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.")
+ return
+ }
+
debuglog("Generating method " + m.symbol.fullName)
method = m
computeLocalVarsIndex(m)
@@ -1607,19 +1613,20 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if (isStaticModule(clasz.symbol)) {
// call object's private ctor from static ctor
lastBlock emit NEW(REFERENCE(m.symbol.enclClass))
- lastBlock emit CALL_METHOD(m.symbol.enclClass.primaryConstructor, Static(true))
+ lastBlock emit CALL_METHOD(m.symbol.enclClass.primaryConstructor, Static(onInstance = true))
}
if (isParcelableClass) { addCreatorCode(lastBlock) }
lastBlock emit RETURN(UNIT)
- lastBlock.close
+ lastBlock.close()
- method = m
+ method = m
jmethod = clinitMethod
jMethodName = CLASS_CONSTRUCTOR_NAME
jmethod.visitCode()
- genCode(m, false, true)
+ computeLocalVarsIndex(m)
+ genCode(m, emitVars = false, isStatic = true)
jmethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments
jmethod.visitEnd()
@@ -1675,7 +1682,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
val kind = toTypeKind(const.typeValue)
val toPush: asm.Type =
if (kind.isValueType) classLiteral(kind)
- else javaType(kind);
+ else javaType(kind)
mv.visitLdcInsn(toPush)
case EnumTag =>
@@ -1698,12 +1705,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
*/
object jcode {
- import asm.Opcodes;
-
- def aconst(cst: AnyRef) {
- if (cst == null) { jmethod.visitInsn(Opcodes.ACONST_NULL) }
- else { jmethod.visitLdcInsn(cst) }
- }
+ import asm.Opcodes
final def boolconst(b: Boolean) { iconst(if(b) 1 else 0) }
@@ -1798,8 +1800,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
def goTo(label: asm.Label) { jmethod.visitJumpInsn(Opcodes.GOTO, label) }
- def emitIF(cond: TestOp, label: asm.Label) { jmethod.visitJumpInsn(cond.opcodeIF, label) }
- def emitIF_ICMP(cond: TestOp, label: asm.Label) { jmethod.visitJumpInsn(cond.opcodeIFICMP, label) }
+ def emitIF(cond: TestOp, label: asm.Label) { jmethod.visitJumpInsn(cond.opcodeIF(), label) }
+ def emitIF_ICMP(cond: TestOp, label: asm.Label) { jmethod.visitJumpInsn(cond.opcodeIFICMP(), label) }
def emitIF_ACMP(cond: TestOp, label: asm.Label) {
assert((cond == EQ) || (cond == NE), cond)
val opc = (if(cond == EQ) Opcodes.IF_ACMPEQ else Opcodes.IF_ACMPNE)
@@ -1855,7 +1857,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
val keyMax = keys(keys.length - 1)
val isDenseEnough: Boolean = {
- /** Calculate in long to guard against overflow. TODO what overflow??? */
+ /* Calculate in long to guard against overflow. TODO what overflow??? */
val keyRangeD: Double = (keyMax.asInstanceOf[Long] - keyMin + 1).asInstanceOf[Double]
val klenD: Double = keys.length
val kdensity: Double = (klenD / keyRangeD)
@@ -1867,10 +1869,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// use a table in which holes are filled with defaultBranch.
val keyRange = (keyMax - keyMin + 1)
val newBranches = new Array[asm.Label](keyRange)
- var oldPos = 0;
+ var oldPos = 0
var i = 0
while(i < keyRange) {
- val key = keyMin + i;
+ val key = keyMin + i
if (keys(oldPos) == key) {
newBranches(i) = branches(oldPos)
oldPos += 1
@@ -1990,7 +1992,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// Part 2 of genCode(): demarcating exception handler boundaries (visitTryCatchBlock() must be invoked before visitLabel() in genBlock())
// ------------------------------------------------------------------------------------------------------------
- /**Generate exception handlers for the current method.
+ /* Generate exception handlers for the current method.
*
* Quoting from the JVMS 4.7.3 The Code Attribute
* The items of the Code_attribute structure are as follows:
@@ -2013,16 +2015,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
*/
def genExceptionHandlers() {
- /** Return a list of pairs of intervals where the handler is active.
- * Each interval is closed on both ends, ie. inclusive both in the left and right endpoints: [start, end].
- * Preconditions:
- * - e.covered non-empty
- * Postconditions for the result:
- * - always non-empty
- * - intervals are sorted as per `linearization`
- * - the argument's `covered` blocks have been grouped into maximally contiguous intervals,
- * ie. between any two intervals in the result there is a non-empty gap.
- * - each of the `covered` blocks in the argument is contained in some interval in the result
+ /* Return a list of pairs of intervals where the handler is active.
+ * Each interval is closed on both ends, ie. inclusive both in the left and right endpoints: [start, end].
+ * Preconditions:
+ * - e.covered non-empty
+ * Postconditions for the result:
+ * - always non-empty
+ * - intervals are sorted as per `linearization`
+ * - the argument's `covered` blocks have been grouped into maximally contiguous intervals,
+ * ie. between any two intervals in the result there is a non-empty gap.
+ * - each of the `covered` blocks in the argument is contained in some interval in the result
*/
def intervals(e: ExceptionHandler): List[BlockInteval] = {
assert(e.covered.nonEmpty, e)
@@ -2069,7 +2071,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// TODO in that case, ExceptionHandler.cls doesn't go through javaName(). What if cls is an inner class?
for (e <- this.method.exh ; if e.covered.nonEmpty ; p <- intervals(e)) {
debuglog("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method +
- " from: " + p.start + " to: " + p.end + " catching: " + e.cls);
+ " from: " + p.start + " to: " + p.end + " catching: " + e.cls)
val cls: String = if (e.cls == NoSymbol || e.cls == ThrowableClass) null
else javaName(e.cls)
jmethod.visitTryCatchBlock(labels(p.start), linNext(p.end), labels(e.startBlock), cls)
@@ -2093,8 +2095,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
def overlaps(that: Interval): Boolean = { !(this.precedes(that) || that.precedes(this)) }
def mergeWith(that: Interval): Interval = {
- val newStart = if(this.start <= that.start) this.lstart else that.lstart;
- val newEnd = if(this.end <= that.end) that.lend else this.lend;
+ val newStart = if(this.start <= that.start) this.lstart else that.lstart
+ val newEnd = if(this.end <= that.end) that.lend else this.lend
Interval(newStart, newEnd)
}
@@ -2150,7 +2152,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
def getMerged(): scala.collection.Map[Local, List[Interval]] = {
// TODO should but isn't: unbalanced start(s) of scope(s)
- val shouldBeEmpty = pending filter { p => val Pair(k, st) = p; st.nonEmpty };
+ val shouldBeEmpty = pending filter { p => val Pair(_, st) = p; st.nonEmpty }
val merged = mutable.Map[Local, List[Interval]]()
def addToMerged(lv: Local, start: Label, end: Label) {
val intv = Interval(start, end)
@@ -2168,10 +2170,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if(merged.isDefinedAt(k)) {
val balancedStart = merged(k).head.lstart
if(balancedStart.getOffset < start.getOffset) {
- start = balancedStart;
+ start = balancedStart
}
}
- val endOpt: Option[Label] = for(ranges <- merged.get(k)) yield ranges.last.lend;
+ val endOpt: Option[Label] = for(ranges <- merged.get(k)) yield ranges.last.lend
val end = endOpt.getOrElse(onePastLast)
addToMerged(k, start, end)
}
@@ -2204,7 +2206,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
for(Pair(local, ranges) <- scoping.getMerged()) {
var name = javaName(local.sym)
if (name == null) {
- anonCounter += 1;
+ anonCounter += 1
name = "<anon" + anonCounter + ">"
}
for(intrvl <- ranges) {
@@ -2213,7 +2215,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
// quest for deterministic output that Map.toList doesn't provide (so that ant test.stability doesn't complain).
val srtd = fltnd.sortBy { kr =>
- val Triple(name: String, local: Local, intrvl: Interval) = kr
+ val Triple(name: String, _, intrvl: Interval) = kr
Triple(intrvl.start, intrvl.end - intrvl.start, name) // ie sort by (start, length, name)
}
@@ -2244,13 +2246,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys)
}
- def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = {
- target.isPublic || target.isProtected && {
- (site.enclClass isSubClass target.enclClass) ||
- (site.enclosingPackage == target.privateWithin)
- }
- } // end of genCode()'s isAccessibleFrom()
-
def genCallMethod(call: CALL_METHOD) {
val CALL_METHOD(method, style) = call
val siteSymbol = clasz.symbol
@@ -2333,6 +2328,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
import asm.Opcodes
(instr.category: @scala.annotation.switch) match {
+
case icodes.localsCat =>
def genLocalInstr() = (instr: @unchecked) match {
case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0)
@@ -2364,14 +2360,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
scoping.popScope(lv, end, instr.pos)
}
}
- genLocalInstr
+ genLocalInstr()
case icodes.stackCat =>
def genStackInstr() = (instr: @unchecked) match {
case LOAD_MODULE(module) =>
// assert(module.isModule, "Expected module: " + module)
- debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags));
+ debuglog("generating LOAD_MODULE for: " + module + " flags: " + module.flagString)
if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) {
jmethod.visitVarInsn(Opcodes.ALOAD, 0)
} else {
@@ -2388,7 +2384,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case LOAD_EXCEPTION(_) => ()
}
- genStackInstr
+ genStackInstr()
case icodes.constCat => genConstant(jmethod, instr.asInstanceOf[CONSTANT].constant)
@@ -2422,11 +2418,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
}
- genCastInstr
+ genCastInstr()
case icodes.objsCat =>
def genObjsInstr() = (instr: @unchecked) match {
-
case BOX(kind) =>
val MethodNameAndType(mname, mdesc) = jBoxTo(kind)
jcode.invokestatic(BoxesRunTime, mname, mdesc)
@@ -2442,14 +2437,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case MONITOR_ENTER() => emit(Opcodes.MONITORENTER)
case MONITOR_EXIT() => emit(Opcodes.MONITOREXIT)
}
- genObjsInstr
+ genObjsInstr()
case icodes.fldsCat =>
def genFldsInstr() = (instr: @unchecked) match {
case lf @ LOAD_FIELD(field, isStatic) =>
- var owner = javaName(lf.hostClass)
- debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags))
+ val owner = javaName(lf.hostClass)
+ debuglog("LOAD_FIELD with owner: " + owner + " flags: " + field.owner.flagString)
val fieldJName = javaName(field)
val fieldDescr = descriptor(field)
val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD
@@ -2463,12 +2458,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr)
}
- genFldsInstr
+ genFldsInstr()
case icodes.mthdsCat =>
def genMethodsInstr() = (instr: @unchecked) match {
- /** Special handling to access native Array.clone() */
+ /* Special handling to access native Array.clone() */
case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
val target: String = javaType(call.targetTypeKind).getInternalName
jcode.invokevirtual(target, "clone", mdesc_arrayClone)
@@ -2476,7 +2471,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case call @ CALL_METHOD(method, style) => genCallMethod(call)
}
- genMethodsInstr
+ genMethodsInstr()
case icodes.arraysCat =>
def genArraysInstr() = (instr: @unchecked) match {
@@ -2485,7 +2480,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case CREATE_ARRAY(elem, 1) => jcode newarray elem
case CREATE_ARRAY(elem, dims) => jmethod.visitMultiANewArrayInsn(descriptor(ArrayN(elem, dims)), dims)
}
- genArraysInstr
+ genArraysInstr()
case icodes.jumpsCat =>
def genJumpInstr() = (instr: @unchecked) match {
@@ -2502,7 +2497,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
while (restTagss.nonEmpty) {
val currLabel = labels(restBranches.head)
for (cTag <- restTagss.head) {
- flatKeys(k) = cTag;
+ flatKeys(k) = cTag
flatBranches(k) = currLabel
k += 1
}
@@ -2515,27 +2510,19 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
jcode.emitSWITCH(flatKeys, flatBranches, defaultLabel, MIN_SWITCH_DENSITY)
case JUMP(whereto) =>
- if (nextBlock != whereto) {
+ if (nextBlock != whereto)
jcode goTo labels(whereto)
- } else if (m.exh.exists(eh => eh.covers(b))) {
// SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH.
// If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range"
- val isSthgLeft = b.toList.exists {
- case _: LOAD_EXCEPTION => false
- case _: SCOPE_ENTER => false
- case _: SCOPE_EXIT => false
- case _: JUMP => false
- case _ => true
- }
- if (!isSthgLeft) {
- emit(asm.Opcodes.NOP)
- }
+ else if (newNormal.isJumpOnly(b) && m.exh.exists(eh => eh.covers(b))) {
+ debugwarn("Had a jump only block that wasn't collapsed")
+ emit(asm.Opcodes.NOP)
}
case CJUMP(success, failure, cond, kind) =>
if (kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
if (nextBlock == success) {
- jcode.emitIF_ICMP(cond.negate, labels(failure))
+ jcode.emitIF_ICMP(cond.negate(), labels(failure))
// .. and fall through to success label
} else {
jcode.emitIF_ICMP(cond, labels(success))
@@ -2543,7 +2530,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
} else if (kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
if (nextBlock == success) {
- jcode.emitIF_ACMP(cond.negate, labels(failure))
+ jcode.emitIF_ACMP(cond.negate(), labels(failure))
// .. and fall through to success label
} else {
jcode.emitIF_ACMP(cond, labels(success))
@@ -2560,7 +2547,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
else emit(Opcodes.DCMPL)
}
if (nextBlock == success) {
- jcode.emitIF(cond.negate, labels(failure))
+ jcode.emitIF(cond.negate(), labels(failure))
// .. and fall through to success label
} else {
jcode.emitIF(cond, labels(success))
@@ -2571,7 +2558,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case CZJUMP(success, failure, cond, kind) =>
if (kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
if (nextBlock == success) {
- jcode.emitIF(cond.negate, labels(failure))
+ jcode.emitIF(cond.negate(), labels(failure))
} else {
jcode.emitIF(cond, labels(success))
if (nextBlock != failure) { jcode goTo labels(failure) }
@@ -2607,7 +2594,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
else emit(Opcodes.DCMPL)
}
if (nextBlock == success) {
- jcode.emitIF(cond.negate, labels(failure))
+ jcode.emitIF(cond.negate(), labels(failure))
} else {
jcode.emitIF(cond, labels(success))
if (nextBlock != failure) { jcode goTo labels(failure) }
@@ -2615,26 +2602,25 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
}
- genJumpInstr
+ genJumpInstr()
case icodes.retCat =>
def genRetInstr() = (instr: @unchecked) match {
case RETURN(kind) => jcode emitRETURN kind
case THROW(_) => emit(Opcodes.ATHROW)
}
- genRetInstr
+ genRetInstr()
}
}
- /**
+ /*
* Emits one or more conversion instructions based on the types given as arguments.
*
* @param from The type of the value to be converted into another type.
* @param to The type the value will be converted into.
*/
def emitT2T(from: TypeKind, to: TypeKind) {
- assert(isNonUnitValueTK(from), from)
- assert(isNonUnitValueTK(to), to)
+ assert(isNonUnitValueTK(from) && isNonUnitValueTK(to), s"Cannot emit primitive conversion from $from to $to")
def pickOne(opcs: Array[Int]) {
val chosen = (to: @unchecked) match {
@@ -2650,10 +2636,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
if(from == to) { return }
- if((from == BOOL) || (to == BOOL)) {
- // the only conversion involving BOOL that is allowed is (BOOL -> BOOL)
- throw new Error("inconvertible types : " + from.toString() + " -> " + to.toString())
- }
+ // the only conversion involving BOOL that is allowed is (BOOL -> BOOL)
+ assert(from != BOOL && to != BOOL, s"inconvertible types : $from -> $to")
if(from.isIntSizedType) { // BYTE, CHAR, SHORT, and INT. (we're done with BOOL already)
@@ -2701,7 +2685,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
def genPrimitive(primitive: Primitive, pos: Position) {
- import asm.Opcodes;
+ import asm.Opcodes
primitive match {
@@ -2732,7 +2716,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
abort("Unknown arithmetic primitive " + primitive)
}
}
- genArith
+ genArith()
// TODO Logical's 2nd elem should be declared ValueTypeKind, to better approximate its allowed values (isIntSized, its comments appears to convey)
// TODO GenICode uses `toTypeKind` to define that elem, `toValueTypeKind` would be needed instead.
@@ -2764,7 +2748,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if (kind != BOOL) { emitT2T(INT, kind) }
}
}
- genLogical
+ genLogical()
case Shift(op, kind) =>
def genShift() = op match {
@@ -2793,7 +2777,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
emitT2T(INT, kind)
}
}
- genShift
+ genShift()
case Comparison(op, kind) =>
def genCompare() = op match {
@@ -2813,12 +2797,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
}
- genCompare
+ genCompare()
case Conversion(src, dst) =>
debuglog("Converting from: " + src + " to: " + dst)
- if (dst == BOOL) { println("Illegal conversion at: " + clasz + " at: " + pos.source + ":" + pos.line) }
- else { emitT2T(src, dst) }
+ emitT2T(src, dst)
case ArrayLength(_) => emit(Opcodes.ARRAYLENGTH)
@@ -2867,15 +2850,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
////////////////////// local vars ///////////////////////
- // def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe))
-
def sizeOf(k: TypeKind): Int = if(k.isWideType) 2 else 1
- // def indexOf(m: IMethod, sym: Symbol): Int = {
- // val Some(local) = m lookupLocal sym
- // indexOf(local)
- // }
-
final def indexOf(local: Local): Int = {
assert(local.index >= 0, "Invalid index for: " + local + "{" + local.## + "}: ")
local.index
@@ -2886,7 +2862,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
* *Does not assume the parameters come first!*
*/
def computeLocalVarsIndex(m: IMethod) {
- var idx = if (m.symbol.isStaticMember) 0 else 1;
+ var idx = if (m.symbol.isStaticMember) 0 else 1
for (l <- m.params) {
debuglog("Index value for " + l + "{" + l.## + "}: " + idx)
@@ -2905,10 +2881,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
/** builder of mirror classes */
- class JMirrorBuilder(bytecodeWriter: BytecodeWriter) extends JCommonBuilder(bytecodeWriter) {
+ class JMirrorBuilder(bytecodeWriter: BytecodeWriter, needsOutfile: Boolean) extends JCommonBuilder(bytecodeWriter, needsOutfile) {
private var cunit: CompilationUnit = _
- def getCurrentCUnit(): CompilationUnit = cunit;
+ def getCurrentCUnit(): CompilationUnit = cunit
/** Generate a mirror class for a top-level module. A mirror class is a class
* containing only static methods that forward to the corresponding method
@@ -2930,7 +2906,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
JAVA_LANG_OBJECT.getInternalName,
EMPTY_STRING_ARRAY)
- log("Dumping mirror class for '%s'".format(mirrorName))
+ log(s"Dumping mirror class for '$mirrorName'")
// typestate: entering mode with valid call sequences:
// [ visitSource ] [ visitOuterClass ] ( visitAnnotation | visitAttribute )*
@@ -2953,13 +2929,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
mirrorClass.visitEnd()
writeIfNotTooBig("" + modsym.name, mirrorName, mirrorClass, modsym)
}
-
-
} // end of class JMirrorBuilder
/** builder of bean info classes */
- class JBeanInfoBuilder(bytecodeWriter: BytecodeWriter) extends JBuilder(bytecodeWriter) {
+ class JBeanInfoBuilder(bytecodeWriter: BytecodeWriter, needsOutfile: Boolean) extends JBuilder(bytecodeWriter, needsOutfile) {
/**
* Generate a bean info class that describes the given class.
@@ -3001,8 +2975,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
for (f <- clasz.fields if f.symbol.hasGetter;
g = f.symbol.getter(clasz.symbol);
- s = f.symbol.setter(clasz.symbol);
- if g.isPublic && !(f.symbol.name startsWith "$")
+ s = f.symbol.setter(clasz.symbol)
+ if g.isPublic && !(f.symbol.name startsWith "$")
) {
// inserting $outer breaks the bean
fieldList = javaName(f.symbol) :: javaName(g) :: (if (s != NoSymbol) javaName(s) else null) :: fieldList
@@ -3091,111 +3065,50 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
* TODO Eventually, these utilities should be moved to IMethod and reused from normalize() (there's nothing JVM-specific about them).
*/
object newNormal {
-
- def startsWithJump(b: BasicBlock): Boolean = { assert(b.nonEmpty, "empty block"); b.firstInstruction.isInstanceOf[JUMP] }
-
- /** Prune from an exception handler those covered blocks which are jump-only. */
- private def coverWhatCountsOnly(m: IMethod): Boolean = {
- assert(m.hasCode, "code-less method")
-
- var wasReduced = false
- for(h <- m.exh) {
- val shouldntCover = (h.covered filter startsWithJump)
- if(shouldntCover.nonEmpty) {
- wasReduced = true
- h.covered --= shouldntCover // not removing any block on purpose.
- }
- }
-
- wasReduced
+ /**
+ * True if a block is "jump only" which is defined
+ * as being a block that consists only of 0 or more instructions that
+ * won't make it to the JVM followed by a JUMP.
+ */
+ def isJumpOnly(b: BasicBlock): Boolean = {
+ val nonICode = firstNonIcodeOnlyInstructions(b)
+ // by definition a block has to have a jump, conditional jump, return, or throw
+ assert(nonICode.hasNext, "empty block")
+ nonICode.next.isInstanceOf[JUMP]
}
- /** An exception handler is pruned provided any of the following holds:
- * (1) it covers nothing (for example, this may result after removing unreachable blocks)
- * (2) each block it covers is of the form: JUMP(_)
- * Return true iff one or more ExceptionHandlers were removed.
- *
- * A caveat: removing an exception handler, for whatever reason, means that its handler code (even if unreachable)
- * won't be able to cause a class-loading-exception. As a result, behavior can be different.
+ /**
+ * Returns the list of instructions in a block that follow all ICode only instructions,
+ * where an ICode only instruction is one that won't make it to the JVM
*/
- private def elimNonCoveringExh(m: IMethod): Boolean = {
- assert(m.hasCode, "code-less method")
-
- def isRedundant(eh: ExceptionHandler): Boolean = {
- (eh.cls != NoSymbol) && ( // TODO `eh.isFinallyBlock` more readable than `eh.cls != NoSymbol`
- eh.covered.isEmpty
- || (eh.covered forall startsWithJump)
- )
- }
-
- var wasReduced = false
- val toPrune = (m.exh.toSet filter isRedundant)
- if(toPrune.nonEmpty) {
- wasReduced = true
- for(h <- toPrune; r <- h.blocks) { m.code.removeBlock(r) } // TODO m.code.removeExh(h)
- m.exh = (m.exh filterNot toPrune)
- }
-
- wasReduced
+ private def firstNonIcodeOnlyInstructions(b: BasicBlock): Iterator[Instruction] = {
+ def isICodeOnlyInstruction(i: Instruction) = i match {
+ case LOAD_EXCEPTION(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) => true
+ case _ => false
+ }
+ b.iterator dropWhile isICodeOnlyInstruction
}
- private def isJumpOnly(b: BasicBlock): Option[BasicBlock] = {
- b.toList match {
- case JUMP(whereto) :: rest =>
- assert(rest.isEmpty, "A block contains instructions after JUMP (looks like enterIgnoreMode() was itself ignored.)")
+ /**
+ * Returns the target of a block that is "jump only" which is defined
+ * as being a block that consists only of 0 or more instructions that
+ * won't make it to the JVM followed by a JUMP.
+ *
+ * @param b The basic block to examine
+ * @return Some(target) if b is a "jump only" block or None if it's not
+ */
+ private def getJumpOnlyTarget(b: BasicBlock): Option[BasicBlock] = {
+ val nonICode = firstNonIcodeOnlyInstructions(b)
+ // by definition a block has to have a jump, conditional jump, return, or throw
+ assert(nonICode.nonEmpty, "empty block")
+ nonICode.next match {
+ case JUMP(whereto) =>
+ assert(!nonICode.hasNext, "A block contains instructions after JUMP (looks like enterIgnoreMode() was itself ignored.)")
Some(whereto)
case _ => None
}
}
- private def directSuccStar(b: BasicBlock): List[BasicBlock] = { directSuccStar(List(b)) }
-
- /** Transitive closure of successors potentially reachable due to normal (non-exceptional) control flow.
- Those BBs in the argument are also included in the result */
- private def directSuccStar(starters: Traversable[BasicBlock]): List[BasicBlock] = {
- val result = new mutable.ListBuffer[BasicBlock]
- var toVisit: List[BasicBlock] = starters.toList.distinct
- while(toVisit.nonEmpty) {
- val h = toVisit.head
- toVisit = toVisit.tail
- result += h
- for(p <- h.directSuccessors; if !result.contains(p) && !toVisit.contains(p)) { toVisit = p :: toVisit }
- }
- result.toList
- }
-
- /** Returns:
- * for single-block self-loops, the pair (start, Nil)
- * for other cycles, the pair (backedge-target, basic-blocks-in-the-cycle-except-backedge-target)
- * otherwise a pair consisting of:
- * (a) the endpoint of a (single or multi-hop) chain of JUMPs
- * (such endpoint does not start with a JUMP and therefore is not part of the chain); and
- * (b) the chain (ie blocks to be removed when collapsing the chain of jumps).
- * Precondition: the BasicBlock given as argument starts with an unconditional JUMP.
- */
- private def finalDestination(start: BasicBlock): (BasicBlock, List[BasicBlock]) = {
- assert(startsWithJump(start), "not the start of a (single or multi-hop) chain of JUMPs.")
- var hops: List[BasicBlock] = Nil
- var prev = start
- var done = false
- do {
- done = isJumpOnly(prev) match {
- case Some(dest) =>
- if (dest == start) { return (start, hops) } // leave infinite-loops in place
- hops ::= prev
- if (hops.contains(dest)) {
- // leave infinite-loops in place
- return (dest, hops filterNot (dest eq _))
- }
- prev = dest;
- false
- case None => true
- }
- } while(!done)
-
- (prev, hops)
- }
-
/**
* Collapse a chain of "jump-only" blocks such as:
*
@@ -3211,7 +3124,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
* In more detail:
* Starting at each of the entry points (m.startBlock, the start block of each exception handler)
* rephrase those control-flow instructions targeting a jump-only block (which jumps to a final destination D) to target D.
- * The blocks thus skipped are also removed from IMethod.blocks.
+ * The blocks thus skipped become eligible to removed by the reachability analyzer
*
* Rationale for this normalization:
* test/files/run/private-inline.scala after -optimize is chock full of
@@ -3222,106 +3135,164 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
* and thus ranges with identical (start, end) (i.e, identical after GenJVM omitted the JUMPs in question)
* could be weeded out to avoid "java.lang.ClassFormatError: Illegal exception table range"
* Now that visitTryCatchBlock() must be called before Labels are resolved,
- * this method gets rid of the BasicBlocks described above (to recap, consisting of just a JUMP).
+ * renders the BasicBlocks described above (to recap, consisting of just a JUMP) unreachable.
*/
- private def collapseJumpOnlyBlocks(m: IMethod): Boolean = {
+ private def collapseJumpOnlyBlocks(m: IMethod) {
assert(m.hasCode, "code-less method")
- /* "start" is relative in a cycle, but we call this helper with the "first" entry-point we found. */
- def realTarget(jumpStart: BasicBlock): Map[BasicBlock, BasicBlock] = {
- assert(startsWithJump(jumpStart), "not part of a jump-chain")
- val Pair(dest, redundants) = finalDestination(jumpStart)
- (for(skipOver <- redundants) yield Pair(skipOver, dest)).toMap
- }
+ def rephraseGotos(detour: mutable.Map[BasicBlock, BasicBlock]) {
+ def lookup(b: BasicBlock) = detour.getOrElse(b, b)
- def rephraseGotos(detour: Map[BasicBlock, BasicBlock]) {
- for(Pair(oldTarget, newTarget) <- detour.iterator) {
- if(m.startBlock == oldTarget) {
- m.code.startBlock = newTarget
- }
- for(eh <- m.exh; if eh.startBlock == oldTarget) {
- eh.setStartBlock(newTarget)
- }
- for(b <- m.blocks; if !detour.isDefinedAt(b)) {
- val idxLast = (b.size - 1)
- b.lastInstruction match {
- case JUMP(whereto) =>
- if (whereto == oldTarget) {
- b.replaceInstruction(idxLast, JUMP(newTarget))
- }
- case CJUMP(succ, fail, cond, kind) =>
- if ((succ == oldTarget) || (fail == oldTarget)) {
- b.replaceInstruction(idxLast, CJUMP(detour.getOrElse(succ, succ),
- detour.getOrElse(fail, fail),
- cond, kind))
- }
- case CZJUMP(succ, fail, cond, kind) =>
- if ((succ == oldTarget) || (fail == oldTarget)) {
- b.replaceInstruction(idxLast, CZJUMP(detour.getOrElse(succ, succ),
- detour.getOrElse(fail, fail),
- cond, kind))
- }
- case SWITCH(tags, labels) =>
- if(labels exists (detour.isDefinedAt(_))) {
- val newLabels = (labels map { lab => detour.getOrElse(lab, lab) })
- b.replaceInstruction(idxLast, SWITCH(tags, newLabels))
- }
- case _ => ()
- }
- }
+ m.code.startBlock = lookup(m.code.startBlock)
+
+ for(eh <- m.exh)
+ eh.setStartBlock(lookup(eh.startBlock))
+
+ for (b <- m.blocks) {
+ def replaceLastInstruction(i: Instruction) = {
+ if (b.lastInstruction != i) {
+ val idxLast = b.size - 1
+ debuglog(s"In block $b, replacing last instruction ${b.lastInstruction} with ${i}")
+ b.replaceInstruction(idxLast, i)
}
}
- /* remove from all containers that may contain a reference to */
- def elide(redu: BasicBlock) {
- assert(m.startBlock != redu, "startBlock should have been re-wired by now")
- m.code.removeBlock(redu);
+ b.lastInstruction match {
+ case JUMP(whereto) =>
+ replaceLastInstruction(JUMP(lookup(whereto)))
+ case CJUMP(succ, fail, cond, kind) =>
+ replaceLastInstruction(CJUMP(lookup(succ), lookup(fail), cond, kind))
+ case CZJUMP(succ, fail, cond, kind) =>
+ replaceLastInstruction(CZJUMP(lookup(succ), lookup(fail), cond, kind))
+ case SWITCH(tags, labels) =>
+ val newLabels = (labels map lookup)
+ replaceLastInstruction(SWITCH(tags, newLabels))
+ case _ => ()
}
+ }
+ }
- var wasReduced = false
- val entryPoints: List[BasicBlock] = m.startBlock :: (m.exh map (_.startBlock));
-
- var elided = mutable.Set.empty[BasicBlock] // debug
- var newTargets = mutable.Set.empty[BasicBlock] // debug
-
- for (ep <- entryPoints) {
- var reachable = directSuccStar(ep) // this list may contain blocks belonging to jump-chains that we'll skip over
- while(reachable.nonEmpty) {
- val h = reachable.head
- reachable = reachable.tail
- if(startsWithJump(h)) {
- val detour = realTarget(h)
- if(detour.nonEmpty) {
- wasReduced = true
- reachable = (reachable filterNot (detour.keySet.contains(_)))
- rephraseGotos(detour)
- detour.keySet foreach elide
- elided ++= detour.keySet
- newTargets ++= detour.values
- }
+ /*
+ * Computes a mapping from jump only block to its
+ * final destination which is either a non-jump-only
+ * block or, if it's in a jump-only block cycle, is
+ * itself
+ */
+ def computeDetour: mutable.Map[BasicBlock, BasicBlock] = {
+ // fetch the jump only blocks and their immediate destinations
+ val pairs = for {
+ block <- m.blocks.toIterator
+ target <- getJumpOnlyTarget(block)
+ } yield(block, target)
+
+ // mapping from a jump-only block to our current knowledge of its
+ // final destination. Initially it's just jump block to immediate jump
+ // target
+ val detour = mutable.Map[BasicBlock, BasicBlock](pairs.toSeq:_*)
+
+ // for each jump-only block find its final destination
+ // taking advantage of the destinations we found for previous
+ // blocks
+ for (key <- detour.keySet) {
+ // we use the Robert Floyd's classic Tortoise and Hare algorithm
+ @tailrec
+ def findDestination(tortoise: BasicBlock, hare: BasicBlock): BasicBlock = {
+ if (tortoise == hare)
+ // cycle detected, map key to key
+ key
+ else if (detour contains hare) {
+ // advance hare once
+ val hare1 = detour(hare)
+ // make sure we can advance hare a second time
+ if (detour contains hare1)
+ // advance tortoise once and hare a second time
+ findDestination(detour(tortoise), detour(hare1))
+ else
+ // hare1 is not in the map so it's not a jump-only block, it's the destination
+ hare1
+ } else
+ // hare is not in the map so it's not a jump-only block, it's the destination
+ hare
}
+ // update the mapping for key based on its final destination
+ detour(key) = findDestination(key, detour(key))
}
+ detour
}
- assert(newTargets.intersect(elided).isEmpty, "contradiction: we just elided the final destionation of a jump-chain")
- wasReduced
+ val detour = computeDetour
+ rephraseGotos(detour)
+
+ if (settings.debug) {
+ val (remappings, cycles) = detour partition {case (source, target) => source != target}
+ for ((source, target) <- remappings) {
+ debuglog(s"Will elide jump only block $source because it can be jumped around to get to $target.")
+ if (m.startBlock == source) debugwarn("startBlock should have been re-wired by now")
+ }
+ val sources = remappings.keySet
+ val targets = remappings.values.toSet
+ val intersection = sources intersect targets
+
+ if (intersection.nonEmpty) debugwarn(s"contradiction: we seem to have some source and target overlap in blocks ${intersection.mkString}. Map was ${detour.mkString}")
+
+ for ((source, _) <- cycles) {
+ debuglog(s"Block $source is in a do-nothing infinite loop. Did the user write 'while(true){}'?")
+ }
+ }
+ }
+
+ /**
+ * Removes all blocks that are unreachable in a method using a standard reachability analysis.
+ */
+ def elimUnreachableBlocks(m: IMethod) {
+ assert(m.hasCode, "code-less method")
+
+ // assume nothing is reachable until we prove it can be reached
+ val reachable = mutable.Set[BasicBlock]()
+
+ // the set of blocks that we know are reachable but have
+ // yet to be marked reachable, initially only the start block
+ val worklist = mutable.Set(m.startBlock)
+
+ while (worklist.nonEmpty) {
+ val block = worklist.head
+ worklist remove block
+ // we know that one is reachable
+ reachable add block
+ // so are its successors, so go back around and add the ones we still
+ // think are unreachable
+ worklist ++= (block.successors filterNot reachable)
+ }
+
+ // exception handlers need to be told not to cover unreachable blocks
+ // and exception handlers that no longer cover any blocks need to be
+ // removed entirely
+ val unusedExceptionHandlers = mutable.Set[ExceptionHandler]()
+ for (exh <- m.exh) {
+ exh.covered = exh.covered filter reachable
+ if (exh.covered.isEmpty) {
+ unusedExceptionHandlers += exh
+ }
+ }
+
+ // remove the unusued exception handler references
+ if (settings.debug)
+ for (exh <- unusedExceptionHandlers) debuglog(s"eliding exception handler $exh because it does not cover any reachable blocks")
+ m.exh = m.exh filterNot unusedExceptionHandlers
+
+ // everything not in the reachable set is unreachable, unused, and unloved. buh bye
+ for (b <- m.blocks filterNot reachable) {
+ debuglog(s"eliding block $b because it is unreachable")
+ m.code removeBlock b
+ }
}
def normalize(m: IMethod) {
if(!m.hasCode) { return }
collapseJumpOnlyBlocks(m)
- var wasReduced = false;
- do {
- wasReduced = false
- // Prune from an exception handler those covered blocks which are jump-only.
- wasReduced |= coverWhatCountsOnly(m); icodes.checkValid(m) // TODO should be unnecessary now that collapseJumpOnlyBlocks(m) is in place
- // Prune exception handlers covering nothing.
- wasReduced |= elimNonCoveringExh(m); icodes.checkValid(m)
-
- // TODO see note in genExceptionHandlers about an ExceptionHandler.covered containing dead blocks (newNormal should remove them, but, where do those blocks come from?)
- } while (wasReduced)
-
- // TODO this would be a good time to remove synthetic local vars seeing no use, don't forget to call computeLocalVarsIndex() afterwards.
+ if (settings.optimise)
+ elimUnreachableBlocks(m)
+ icodes checkValid m
}
}